diff --git a/authn/authenticate.go b/authn/authenticate.go index bb207560e..64e8e5a9e 100644 --- a/authn/authenticate.go +++ b/authn/authenticate.go @@ -30,13 +30,13 @@ type Auth struct { oAuthConfig *oauth2.Config } -func NewAuth(logger logger.Logger, url string, scopes []string, authUrl, tokenUrl, groupEndpoint, clientID, clientSecret, hashKey, blockKey string) (*Auth, error) { +func NewAuth(logger logger.Logger, url string, scopes []string, authUrl, tokenUrl, groupEndpoint, logout, clientID, clientSecret, hashKey, blockKey string) (*Auth, error) { auth := &Auth{ logger: logger, cookieName: "molecula-chip", refreshWithin: time.Minute * time.Duration(15), groupEndpoint: groupEndpoint, - logoutEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/logout", + logoutEndpoint: logout, fbURL: url, oAuthConfig: &oauth2.Config{ RedirectURL: fmt.Sprintf("%s/redirect", url), @@ -172,13 +172,13 @@ func (a *Auth) newCookieValue(token *oauth2.Token) (*CookieValue, error) { } accessParsed, err := jwt.Parse(token.AccessToken, nil) if token == nil { - fmt.Println(errors.Wrap(err, "parsing jwt claims from access tokens")) + a.logger.Errorf("parsing jwt claims from access tokens: %v", err) } claims := accessParsed.Claims.(jwt.MapClaims) groups, err := a.getGroupMembership(token) if err != nil { - fmt.Println(errors.Wrap(err, "getting group memebership")) + a.logger.Errorf("getting group memebership %v", err) } // not needed at this point in the logic and makes the encoded cookie too large token.AccessToken = "" @@ -194,6 +194,10 @@ func (a *Auth) getGroupMembership(token *oauth2.Token) (Groups, error) { var groups Groups var bearer = fmt.Sprintf("Bearer %s", token.AccessToken) req, err := http.NewRequest("GET", a.groupEndpoint, nil) + if err != nil { + return groups, errors.Wrap(err, "creating new request to group endpoint") + } + req.Header.Add("Authorization", bearer) client := &http.Client{} response, err := client.Do(req) @@ -248,7 +252,6 @@ func (a *Auth) setCookie(w http.ResponseWriter, cookie *CookieValue) error { } func (a *Auth) refreshToken(w http.ResponseWriter, cookie *CookieValue) error { - fmt.Println("REFRESHING TOKEN") if cookie.Token.RefreshToken == "" { return errors.New("no refresh token found, check auth scopes to see if refresh tokens are being provided by your IdP.") } @@ -258,8 +261,6 @@ func (a *Auth) refreshToken(w http.ResponseWriter, cookie *CookieValue) error { return errors.Wrap(err, "refreshing token") } - fmt.Printf("Refreshed AT: %v\n\n", newToken.AccessToken) - if newToken.Expiry != cookie.Token.Expiry { cv, err := a.newCookieValue(newToken) if err != nil { @@ -267,7 +268,6 @@ func (a *Auth) refreshToken(w http.ResponseWriter, cookie *CookieValue) error { } a.setCookie(w, cv) - fmt.Println("refreshed access token") } return nil diff --git a/authn/authenticate_test.go b/authn/authenticate_test.go index f001e4a56..97ede1da1 100644 --- a/authn/authenticate_test.go +++ b/authn/authenticate_test.go @@ -22,6 +22,7 @@ func TestAuth(t *testing.T) { settings.Auth.AuthorizeURL = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/authorize" settings.Auth.TokenURL = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/token" settings.Auth.GroupEndpointURL = "https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true" + settings.Auth.LogoutURL = "https://login.microsoftonline.com/common/oauth2/v2.0/logout" settings.Auth.Scopes = []string{"https://graph.microsoft.com/.default", "offline_access"} settings.Auth.HashKey = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF" settings.Auth.BlockKey = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF" @@ -33,6 +34,7 @@ func TestAuth(t *testing.T) { settings.Auth.AuthorizeURL, settings.Auth.TokenURL, settings.Auth.GroupEndpointURL, + settings.Auth.LogoutURL, settings.Auth.ClientId, settings.Auth.ClientSecret, settings.Auth.HashKey, diff --git a/ctl/server.go b/ctl/server.go index 627f3e916..6e65c6ef1 100644 --- a/ctl/server.go +++ b/ctl/server.go @@ -116,6 +116,7 @@ func BuildServerFlags(cmd *cobra.Command, srv *server.Command) { flags.StringVar(&srv.Config.Auth.AuthorizeURL, "auth.authorize-url", srv.Config.Auth.AuthorizeURL, "Identity Provider's Authorize URL.") flags.StringVar(&srv.Config.Auth.TokenURL, "auth.token-url", srv.Config.Auth.TokenURL, "Identity Provider's Token URL.") flags.StringVar(&srv.Config.Auth.GroupEndpointURL, "auth.group-endpoint-url", srv.Config.Auth.GroupEndpointURL, "Identity Provider's Group endpoint URL.") + flags.StringVar(&srv.Config.Auth.LogoutURL, "auth.logout-url", srv.Config.Auth.LogoutURL, "Identity Provider's Logout URL.") flags.StringSliceVar(&srv.Config.Auth.Scopes, "auth.scopes", srv.Config.Auth.Scopes, "Comma separated list of scopes obtained from IdP") flags.StringVar(&srv.Config.Auth.HashKey, "auth.hash-key", srv.Config.Auth.HashKey, "First Secret for Auth.") flags.StringVar(&srv.Config.Auth.BlockKey, "auth.block-key", srv.Config.Auth.BlockKey, "Second Secret for Auth.") diff --git a/http/handler_internal_test.go b/http/handler_internal_test.go index f0db77695..c184e4523 100644 --- a/http/handler_internal_test.go +++ b/http/handler_internal_test.go @@ -178,25 +178,43 @@ func TestFieldOptionValidation(t *testing.T) { } } +func readResponse(w *httptest.ResponseRecorder) ([]byte, error) { + res := w.Result() + defer res.Body.Close() + return ioutil.ReadAll(res.Body) +} + func TestAuth(t *testing.T) { + var ( + ClientId = "e9088663-eb08-41d7-8f65-efb5f54bbb71" + ClientSecret = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF" + AuthorizeURL = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/authorize" + TokenURL = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/token" + GroupEndpointURL = "https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true" + LogoutURL = "https://login.microsoftonline.com/common/oauth2/v2.0/logout" + Scopes = []string{"https://graph.microsoft.com/.default", "offline_access"} + HashKey = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF" + BlockKey = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF" + ) + hashKey, _ := hex.DecodeString("DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF") blockKey, _ := hex.DecodeString("DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF") a, err := authn.NewAuth( logger.NewStandardLogger(os.Stdout), "http://localhost:10101/", - []string{"https://graph.microsoft.com/.default", "offline_access"}, - "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/authorize", - "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/token", - "https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true", - "e9088663-eb08-41d7-8f65-efb5f54bbb71", - "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", - "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", - "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", + Scopes, + AuthorizeURL, + TokenURL, + GroupEndpointURL, + LogoutURL, + ClientId, + ClientSecret, + HashKey, + BlockKey, ) - if err != nil { - t.Errorf("building auth object %s", err) + t.Errorf("building auth object%s", err) } h := Handler{ @@ -235,30 +253,21 @@ func TestAuth(t *testing.T) { Expires: validToken.Expiry, } - t.Run("Login-Cookie", func(t *testing.T) { + t.Run("Login", func(t *testing.T) { r := httptest.NewRequest(gohttp.MethodGet, "/login", nil) w := httptest.NewRecorder() - h.handleLogin(w, r) - - }) - t.Run("Login-NoCookie", func(t *testing.T) { - r := httptest.NewRequest(gohttp.MethodGet, "/login", nil) - w := httptest.NewRecorder() - r.AddCookie(validCookie) - //login w/o cookie h.handleLogin(w, r) - res := w.Result() - defer res.Body.Close() - data, err := ioutil.ReadAll(res.Body) + data, err := readResponse(w) if err != nil { t.Errorf("expected no errors reading response, got: %+v", err) } - fmt.Printf("%s", data) - - //login with cookie + fmt.Printf("%d", strings.Index(string(data), AuthorizeURL)) + if strings.Index(string(data), AuthorizeURL) != 9 { + t.Errorf("incorrect redirect url: expected: %s, got: %s", AuthorizeURL, string(data)) + } }) t.Run("Login-BadCookie", func(t *testing.T) { r := httptest.NewRequest(gohttp.MethodGet, "/login", nil) @@ -280,9 +289,12 @@ func TestAuth(t *testing.T) { t.Run("Logout", func(t *testing.T) { r := httptest.NewRequest(gohttp.MethodGet, "/logout", nil) w := httptest.NewRecorder() + r.AddCookie(validCookie) h.handleLogout(w, r) + fmt.Println() + //logout with cookie //logout without cookie diff --git a/install/featurebase.conf b/install/featurebase.conf index a071f95f9..c824098df 100644 --- a/install/featurebase.conf +++ b/install/featurebase.conf @@ -378,9 +378,10 @@ log-path = "/var/log/molecula/featurebase.log" # enable = false # client-id = "" # client-secret = "" -# authorize-url = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/authorize" -# token-url = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/token" -# group-endpoint-url = "https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true" -# scopes = ["https://graph.microsoft.com/.default", "offline_access"] +# authorize-url = "" +# token-url = "" +# group-endpoint-url = "" +# logout-url = "" +# scopes = ["", ""] # hash-key = "" # block-key = "" \ No newline at end of file diff --git a/server/config.go b/server/config.go index fd50b019d..45cf56a54 100644 --- a/server/config.go +++ b/server/config.go @@ -243,6 +243,7 @@ type Auth struct { AuthorizeURL string `toml:"authorize-url"` TokenURL string `toml:"token-url"` GroupEndpointURL string `toml:"group-endpoint-url"` + LogoutURL string `toml:"logout-url"` Scopes []string `toml:"scopes"` HashKey string `toml:"hash-key"` BlockKey string `toml:"block-key"` @@ -620,6 +621,7 @@ func (c *Config) ValidateAuth() ([]error, error) { "AuthorizeURL": c.Auth.AuthorizeURL, "TokenURL": c.Auth.TokenURL, "GroupEndpointURL": c.Auth.GroupEndpointURL, + "LogoutURL": c.Auth.LogoutURL, "HashKey": c.Auth.HashKey, "BlockKey": c.Auth.BlockKey, } diff --git a/server/config_internal_test.go b/server/config_internal_test.go index 50332d51f..ff262ee4b 100644 --- a/server/config_internal_test.go +++ b/server/config_internal_test.go @@ -308,6 +308,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, errorMesgEmpty, + errorMesgEmpty, errorMesgScope, }, Auth{ @@ -317,6 +318,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: emptyString, TokenURL: emptyString, GroupEndpointURL: emptyString, + LogoutURL: emptyString, Scopes: emptySlice, HashKey: emptyString, BlockKey: emptyString, @@ -331,6 +333,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, errorMesgEmpty, + errorMesgEmpty, errorMesgScope, }, Auth{ @@ -340,6 +343,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: emptyString, TokenURL: emptyString, GroupEndpointURL: emptyString, + LogoutURL: emptyString, Scopes: emptySlice, HashKey: emptyString, BlockKey: emptyString, @@ -354,6 +358,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, errorMesgEmpty, + errorMesgEmpty, errorMesgScope, }, Auth{ @@ -363,6 +368,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: emptyString, TokenURL: emptyString, GroupEndpointURL: emptyString, + LogoutURL: emptyString, Scopes: emptySlice, HashKey: emptyString, BlockKey: emptyString, @@ -376,6 +382,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, errorMesgEmpty, + errorMesgURL, errorMesgScope, }, Auth{ @@ -385,6 +392,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: emptyString, TokenURL: emptyString, GroupEndpointURL: emptyString, + LogoutURL: notValidURL, Scopes: emptySlice, HashKey: emptyString, BlockKey: emptyString, @@ -397,6 +405,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, errorMesgEmpty, + errorMesgEmpty, errorMesgScope, }, Auth{ @@ -406,6 +415,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: validTestURL, TokenURL: emptyString, GroupEndpointURL: emptyString, + LogoutURL: emptyString, Scopes: emptySlice, HashKey: emptyString, BlockKey: emptyString, @@ -426,6 +436,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: validTestURL, TokenURL: validTestURL, GroupEndpointURL: emptyString, + LogoutURL: validTestURL, Scopes: emptySlice, HashKey: emptyString, BlockKey: emptyString, @@ -437,6 +448,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, errorMesgURL, + errorMesgURL, }, Auth{ Enable: enable, @@ -445,6 +457,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: notValidURL, TokenURL: validTestURL, GroupEndpointURL: validTestURL, + LogoutURL: notValidURL, Scopes: validStringSlice, HashKey: emptyString, BlockKey: emptyString, @@ -453,6 +466,7 @@ func TestConfig_validateAuth(t *testing.T) { { // Auth enabled, some strings are set to invalid URL []string{ + errorMesgURL, errorMesgURL, errorMesgURL, errorMesgEmpty, @@ -464,6 +478,7 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: validTestURL, TokenURL: notValidURL, GroupEndpointURL: notValidURL, + LogoutURL: notValidURL, Scopes: validStringSlice, HashKey: emptyString, BlockKey: validKey, @@ -479,21 +494,23 @@ func TestConfig_validateAuth(t *testing.T) { AuthorizeURL: validTestURL, TokenURL: validTestURL, GroupEndpointURL: validTestURL, + LogoutURL: validTestURL, Scopes: validStringSlice, HashKey: validKey, BlockKey: validKey, }, }, { - // Auth disabled, all configs are set to valid values + // Auth disabled, all configs are set to some values []string{}, Auth{ Enable: disable, ClientId: validString, ClientSecret: validString, AuthorizeURL: validString, - TokenURL: validString, - GroupEndpointURL: validString, + TokenURL: validTestURL, + GroupEndpointURL: validTestURL, + LogoutURL: validTestURL, Scopes: validStringSlice, HashKey: validKey, BlockKey: validKey, diff --git a/server/server.go b/server/server.go index d5fefc2db..2b2f4ddc8 100644 --- a/server/server.go +++ b/server/server.go @@ -525,7 +525,7 @@ func (m *Command) SetupServer() error { if m.Config.Auth.Enable { m.Config.MustValidateAuth() ac := m.Config.Auth - m.auth, err = authn.NewAuth(m.logger, m.listenURI.String(), ac.Scopes, ac.AuthorizeURL, ac.TokenURL, ac.GroupEndpointURL, ac.ClientId, ac.ClientSecret, ac.HashKey, ac.BlockKey) + m.auth, err = authn.NewAuth(m.logger, m.listenURI.String(), ac.Scopes, ac.AuthorizeURL, ac.TokenURL, ac.GroupEndpointURL, ac.LogoutURL, ac.ClientId, ac.ClientSecret, ac.HashKey, ac.BlockKey) if err != nil { return errors.Wrap(err, "instantiating authN object") }