feat/role based access (#50)

* feat: add roles based access

* feat: update roles env + todo

* feat: add roles to update profile

* feat: add role based oauth

* feat: validate role for a given token
This commit is contained in:
Lakhan Samani
2021-09-20 10:36:26 +05:30
committed by GitHub
parent 195270525c
commit 21e3425e76
28 changed files with 544 additions and 141 deletions

View File

@@ -25,7 +25,6 @@ func AppHandler() gin.HandlerFunc {
if state == "" {
// cookie, err := utils.GetAuthToken(c)
// log.Println(`cookie`, cookie)
// if err != nil {
// c.JSON(400, gin.H{"error": "invalid state"})
// return
@@ -67,13 +66,6 @@ func AppHandler() gin.HandlerFunc {
}
}
log.Println(gin.H{
"data": map[string]string{
"authorizerURL": stateObj.AuthorizerURL,
"redirectURL": stateObj.RedirectURL,
},
})
// debug the request state
if pusher := c.Writer.Pusher(); pusher != nil {
// use pusher.Push() to do server push

View File

@@ -19,7 +19,7 @@ import (
"golang.org/x/oauth2"
)
func processGoogleUserInfo(code string, c *gin.Context) error {
func processGoogleUserInfo(code string, role string, c *gin.Context) error {
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid google exchange code: %s", err.Error())
@@ -50,6 +50,7 @@ func processGoogleUserInfo(code string, c *gin.Context) error {
if err != nil {
// user not registered, register user and generate session token
user.SignupMethod = enum.Google.String()
user.Roles = role
} else {
// user exists in db, check if method was google
// if not append google to existing signup method and save it
@@ -60,27 +61,26 @@ func processGoogleUserInfo(code string, c *gin.Context) error {
}
user.SignupMethod = signupMethod
user.Password = existingUser.Password
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
return fmt.Errorf("invalid role")
}
user.Roles = existingUser.Roles
}
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
}
func processGithubUserInfo(code string, c *gin.Context) error {
func processGithubUserInfo(code string, role string, c *gin.Context) error {
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid github exchange code: %s", err.Error())
@@ -128,6 +128,7 @@ func processGithubUserInfo(code string, c *gin.Context) error {
if err != nil {
// user not registered, register user and generate session token
user.SignupMethod = enum.Github.String()
user.Roles = role
} else {
// user exists in db, check if method was google
// if not append google to existing signup method and save it
@@ -138,26 +139,26 @@ func processGithubUserInfo(code string, c *gin.Context) error {
}
user.SignupMethod = signupMethod
user.Password = existingUser.Password
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
return fmt.Errorf("invalid role")
}
user.Roles = existingUser.Roles
}
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
}
func processFacebookUserInfo(code string, c *gin.Context) error {
func processFacebookUserInfo(code string, role string, c *gin.Context) error {
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid facebook exchange code: %s", err.Error())
@@ -199,6 +200,7 @@ func processFacebookUserInfo(code string, c *gin.Context) error {
if err != nil {
// user not registered, register user and generate session token
user.SignupMethod = enum.Github.String()
user.Roles = role
} else {
// user exists in db, check if method was google
// if not append google to existing signup method and save it
@@ -209,20 +211,20 @@ func processFacebookUserInfo(code string, c *gin.Context) error {
}
user.SignupMethod = signupMethod
user.Password = existingUser.Password
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
return fmt.Errorf("invalid role")
}
user.Roles = existingUser.Roles
}
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
@@ -238,23 +240,27 @@ func OAuthCallbackHandler() gin.HandlerFunc {
c.JSON(400, gin.H{"error": "invalid oauth state"})
}
session.DeleteToken(sessionState)
// contains random token, redirect url, role
sessionSplit := strings.Split(state, "___")
// TODO validate redirect url
if len(sessionSplit) != 2 {
if len(sessionSplit) < 2 {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
role := sessionSplit[2]
redirectURL := sessionSplit[1]
var err error
code := c.Request.FormValue("code")
switch provider {
case enum.Google.String():
err = processGoogleUserInfo(code, c)
err = processGoogleUserInfo(code, role, c)
case enum.Github.String():
err = processGithubUserInfo(code, c)
err = processGithubUserInfo(code, role, c)
case enum.Facebook.String():
err = processFacebookUserInfo(code, c)
err = processFacebookUserInfo(code, role, c)
default:
err = fmt.Errorf(`invalid oauth provider`)
}
@@ -263,6 +269,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.Redirect(http.StatusTemporaryRedirect, sessionSplit[1])
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
@@ -17,6 +18,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
return func(c *gin.Context) {
// TODO validate redirect URL
redirectURL := c.Query("redirectURL")
role := c.Query("role")
if redirectURL == "" {
c.JSON(400, gin.H{
@@ -24,8 +26,21 @@ func OAuthLoginHandler() gin.HandlerFunc {
})
return
}
if role != "" {
// validate role
if !utils.IsValidRole(constants.ROLES, role) {
c.JSON(400, gin.H{
"error": "invalid role",
})
return
}
} else {
role = constants.DEFAULT_ROLE
}
uuid := uuid.New()
oauthStateString := uuid.String() + "___" + redirectURL
oauthStateString := uuid.String() + "___" + redirectURL + "___" + role
provider := c.Param("oauth_provider")

View File

@@ -50,15 +50,9 @@ func VerifyEmailHandler() gin.HandlerFunc {
db.Mgr.DeleteToken(claim.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, user.Roles)
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, user.Roles)
session.SetToken(userIdStr, refreshToken)
utils.SetCookie(c, accessToken)