NET-1227: Add Additional Oauth Scopes to fetch user email (#3079)

* add list roles to pro and ce

* if not pro set user role to admin

* validate update user

* add separate validation check for password on update

* remove validate check

* fix github SSO with invite signup

* add oauth scopes for user email

* remove debug log

* fix azure ad
This commit is contained in:
Abhishek K 2024-08-28 14:14:26 +05:30 committed by GitHub
parent 9ac78e15bc
commit 0463b17ea5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 26 additions and 24 deletions

3
go.mod
View file

@ -38,11 +38,9 @@ require (
)
require (
github.com/go-jose/go-jose/v3 v3.0.3
github.com/guumaster/tablewriter v0.0.10
github.com/matryer/is v1.4.1
github.com/olekukonko/tablewriter v0.0.5
github.com/resendlabs/resend-go v1.7.0
github.com/spf13/cobra v1.8.1
gopkg.in/mail.v2 v2.3.1
)
@ -50,6 +48,7 @@ require (
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/seancfoley/bintree v1.3.1 // indirect

12
go.sum
View file

@ -14,8 +14,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
@ -65,10 +63,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0 h1:Y2hUrkfuM0on62KZOci/VLijlkdF/yeWU262BQgvcjE=
github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
github.com/posthog/posthog-go v1.2.18 h1:2CBA0LOB0up+gon+xpeXuhFw69gZpjAYxQoBBGwiDWw=
github.com/posthog/posthog-go v1.2.18/go.mod h1:QjlpryJtfYLrZF2GUkAhejH4E7WlDbdKkvOi5hLmkdg=
github.com/resendlabs/resend-go v1.7.0 h1:DycOqSXtw2q7aB+Nt9DDJUDtaYcrNPGn1t5RFposas0=
github.com/resendlabs/resend-go v1.7.0/go.mod h1:yip1STH7Bqfm4fD0So5HgyNbt5taG5Cplc4xXxETyLI=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -102,8 +96,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -115,8 +107,6 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -134,8 +124,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

View file

@ -6,7 +6,6 @@ import (
"strings"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
)
@ -27,12 +26,10 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
r.Header.Set("ismaster", "no")
logger.Log(0, "next", r.URL.String())
isGlobalAccesss := r.Header.Get("IS_GLOBAL_ACCESS") == "yes"
bearerToken := r.Header.Get("Authorization")
username, err := GetUserNameFromToken(bearerToken)
if err != nil {
logger.Log(0, "next 1", r.URL.String(), err.Error())
ReturnErrorResponse(w, r, FormatError(err, "unauthorized"))
return
}
@ -103,7 +100,6 @@ func ContinueIfUserMatch(next http.Handler) http.HandlerFunc {
requestedUser, _ = url.QueryUnescape(r.URL.Query().Get("username"))
}
if requestedUser != r.Header.Get("user") {
logger.Log(0, "next 2", r.URL.String(), errorResponse.Message)
ReturnErrorResponse(w, r, errorResponse)
return
}

View file

@ -7,6 +7,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/gorilla/websocket"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
@ -236,6 +237,17 @@ func getStateAndCode(r *http.Request) (string, string) {
return state, code
}
func getUserEmailFromClaims(token string) string {
accessToken, _ := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(""), nil
})
if accessToken == nil {
return ""
}
claims, _ := accessToken.Claims.(jwt.MapClaims)
return claims["email"].(string)
}
func (user *OAuthUser) getUserName() string {
var userName string
if user.Email != "" {

View file

@ -33,7 +33,7 @@ func initAzureAD(redirectURL string, clientID string, clientSecret string) {
RedirectURL: redirectURL,
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: []string{"User.Read"},
Scopes: []string{"User.Read", "email", "profile", "openid"},
Endpoint: microsoft.AzureADEndpoint(servercfg.GetAzureTenant()),
}
}
@ -67,10 +67,9 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
handleOauthNotConfigured(w)
return
}
var inviteExists bool
// check if invite exists for User
in, err := logic.GetUserInvite(content.UserPrincipalName)
in, err := logic.GetUserInvite(content.Email)
if err == nil {
inviteExists = true
}
@ -90,11 +89,12 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
user.UserName = content.UserPrincipalName // override username with azure id
if err = logic.CreateUser(&user); err != nil {
handleSomethingWentWrong(w)
return
}
logic.DeleteUserInvite(user.UserName)
logic.DeleteUserInvite(content.Email)
logic.DeletePendingUser(content.UserPrincipalName)
} else {
if !isEmailAllowed(content.UserPrincipalName) {
@ -166,8 +166,9 @@ func getAzureUserInfo(state string, code string) (*OAuthUser, error) {
}
var httpReq, reqErr = http.NewRequest("GET", "https://graph.microsoft.com/v1.0/me", nil)
if reqErr != nil {
return nil, fmt.Errorf("failed to create request to GitHub")
return nil, fmt.Errorf("failed to create request to microsoft")
}
httpReq.Header.Set("Authorization", "Bearer "+token.AccessToken)
response, err := http.DefaultClient.Do(httpReq)
if err != nil {
@ -183,6 +184,9 @@ func getAzureUserInfo(state string, code string) (*OAuthUser, error) {
return nil, fmt.Errorf("failed parsing email from response data: %s", err.Error())
}
userInfo.AccessToken = string(data)
if userInfo.Email == "" {
userInfo.Email = getUserEmailFromClaims(token.AccessToken)
}
return userInfo, nil
}

View file

@ -33,7 +33,7 @@ func initGithub(redirectURL string, clientID string, clientSecret string) {
RedirectURL: redirectURL,
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: []string{},
Scopes: []string{"read:user", "user:email"},
Endpoint: github.Endpoint,
}
}
@ -186,6 +186,9 @@ func getGithubUserInfo(state string, code string) (*OAuthUser, error) {
return nil, fmt.Errorf("failed parsing email from response data: %s", err.Error())
}
userInfo.AccessToken = string(data)
if userInfo.Email == "" {
userInfo.Email = getUserEmailFromClaims(token.AccessToken)
}
return userInfo, nil
}