From 2ce3b86e0ff69538935db3149d1ed2f24aea09a3 Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Mon, 13 Apr 2020 23:57:13 -0400 Subject: Simplify --- server/auth/microsoft/microsoft.go | 201 ------------------------------------- 1 file changed, 201 deletions(-) delete mode 100644 server/auth/microsoft/microsoft.go (limited to 'server/auth/microsoft/microsoft.go') diff --git a/server/auth/microsoft/microsoft.go b/server/auth/microsoft/microsoft.go deleted file mode 100644 index 8463ccf..0000000 --- a/server/auth/microsoft/microsoft.go +++ /dev/null @@ -1,201 +0,0 @@ -package microsoft - -import ( - "encoding/json" - "errors" - "net/http" - "path" - "strings" - - "github.com/nsheridan/cashier/server/auth" - "github.com/nsheridan/cashier/server/config" - "github.com/nsheridan/cashier/server/metrics" - - "golang.org/x/oauth2" - "golang.org/x/oauth2/microsoft" -) - -const ( - name = "microsoft" -) - -// Config is an implementation of `auth.Provider` for authenticating using a -// Office 365 account. -type Config struct { - config *oauth2.Config - tenant string - groups map[string]bool - whitelist map[string]bool -} - -var _ auth.Provider = (*Config)(nil) - -// New creates a new Microsoft provider from a configuration. -func New(c *config.Auth) (*Config, error) { - whitelist := make(map[string]bool) - for _, u := range c.UsersWhitelist { - whitelist[u] = true - } - if c.ProviderOpts["tenant"] == "" && len(whitelist) == 0 { - return nil, errors.New("either Office 365 tenant or users whitelist must be specified") - } - groupMap := make(map[string]bool) - if groups, ok := c.ProviderOpts["groups"]; ok { - for _, group := range strings.Split(groups, ",") { - groupMap[strings.Trim(group, " ")] = true - } - } - - return &Config{ - config: &oauth2.Config{ - ClientID: c.OauthClientID, - ClientSecret: c.OauthClientSecret, - RedirectURL: c.OauthCallbackURL, - Endpoint: microsoft.AzureADEndpoint(c.ProviderOpts["tenant"]), - Scopes: []string{"user.Read.All", "Directory.Read.All"}, - }, - tenant: c.ProviderOpts["tenant"], - whitelist: whitelist, - groups: groupMap, - }, nil -} - -// A new oauth2 http client. -func (c *Config) newClient(token *oauth2.Token) *http.Client { - return c.config.Client(oauth2.NoContext, token) -} - -// Gets a response for an graph api call. -func (c *Config) getDocument(token *oauth2.Token, pathElements ...string) map[string]interface{} { - client := c.newClient(token) - url := "https://" + path.Join("graph.microsoft.com/v1.0", path.Join(pathElements...)) - resp, err := client.Get(url) - if err != nil { - return nil - } - defer resp.Body.Close() - var document map[string]interface{} - if err := json.NewDecoder(resp.Body).Decode(&document); err != nil { - return nil - } - return document -} - -// Get info from the "/me" endpoint of the Microsoft Graph API (MSG-API). -// https://developer.microsoft.com/en-us/graph/docs/concepts/v1-overview -func (c *Config) getMe(token *oauth2.Token, item string) string { - document := c.getDocument(token, "/me") - if value, ok := document[item].(string); ok { - return value - } - return "" -} - -// Check against verified domains from "/organization" endpoint of MSG-API. -func (c *Config) verifyTenant(token *oauth2.Token) bool { - document := c.getDocument(token, "/organization") - // The domains for an organisation are in an array of structs under - // verifiedDomains, which is in a struct which is in turn an array - // of such structs under value in the document. Which in json looks - // like this: - // { "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#organization", - // "value": [ { - // ... - // "verifiedDomains": [ { - // ... - // "name": "M365x214355.onmicrosoft.com", - // } ] - // } ] - //} - var value []interface{} - var ok bool - if value, ok = document["value"].([]interface{}); !ok { - return false - } - for _, valueEntry := range value { - if value, ok = valueEntry.(map[string]interface{})["verifiedDomains"].([]interface{}); !ok { - continue - } - for _, val := range value { - domain := val.(map[string]interface{})["name"].(string) - if domain == c.tenant { - return true - } - } - } - return false -} - -// Check against groups from /users/{id}/memberOf endpoint of MSG-API. -func (c *Config) verifyGroups(token *oauth2.Token) bool { - document := c.getDocument(token, "/users/me/memberOf") - var value []interface{} - var ok bool - if value, ok = document["value"].([]interface{}); !ok { - return false - } - for _, valueEntry := range value { - if group, ok := valueEntry.(map[string]interface{})["displayName"].(string); ok { - if c.groups[group] { - return true - } - } - } - return false -} - -// Name returns the name of the provider. -func (c *Config) Name() string { - return name -} - -// Valid validates the oauth token. -func (c *Config) Valid(token *oauth2.Token) bool { - if len(c.whitelist) > 0 && !c.whitelist[c.Email(token)] { - return false - } - if !token.Valid() { - return false - } - metrics.M.AuthValid.WithLabelValues("microsoft").Inc() - if c.tenant != "" { - if c.verifyTenant(token) { - if len(c.groups) > 0 { - return c.verifyGroups(token) - } - return true - } - } - return false -} - -// Revoke disables the access token. -func (c *Config) Revoke(token *oauth2.Token) error { - return nil -} - -// StartSession retrieves an authentication endpoint from Microsoft. -func (c *Config) StartSession(state string) string { - return c.config.AuthCodeURL(state, - oauth2.SetAuthURLParam("hd", c.tenant), - oauth2.SetAuthURLParam("prompt", "login")) -} - -// Exchange authorizes the session and returns an access token. -func (c *Config) Exchange(code string) (*oauth2.Token, error) { - t, err := c.config.Exchange(oauth2.NoContext, code) - if err == nil { - metrics.M.AuthExchange.WithLabelValues("microsoft").Inc() - } - return t, err -} - -// Email retrieves the email address of the user. -func (c *Config) Email(token *oauth2.Token) string { - return c.getMe(token, "mail") -} - -// Username retrieves the username portion of the user's email address. -func (c *Config) Username(token *oauth2.Token) string { - return strings.Split(c.Email(token), "@")[0] -} -- cgit v1.2.3