aboutsummaryrefslogtreecommitdiff
path: root/server/auth/microsoft/microsoft.go
diff options
context:
space:
mode:
Diffstat (limited to 'server/auth/microsoft/microsoft.go')
-rw-r--r--server/auth/microsoft/microsoft.go201
1 files changed, 0 insertions, 201 deletions
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]
-}