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

@@ -46,16 +46,19 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
log.Println("Compare password error:", err)
return res, fmt.Errorf(`invalid password`)
}
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
role := constants.DEFAULT_ROLE
if params.Role != nil {
// validate role
if !utils.IsValidRole(strings.Split(user.Roles, ","), *params.Role) {
return res, fmt.Errorf(`invalid role`)
}
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
role = *params.Role
}
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
session.SetToken(userIdStr, refreshToken)
@@ -71,6 +74,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
},

View File

@@ -2,6 +2,7 @@ package resolvers
import (
"context"
"fmt"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session"
@@ -25,7 +26,8 @@ func Logout(ctx context.Context) (*model.Response, error) {
return res, err
}
session.DeleteToken(claim.ID)
userId := fmt.Sprintf("%v", claim["id"])
session.DeleteToken(userId)
res = &model.Response{
Message: "Logged out successfully",
}

View File

@@ -3,6 +3,7 @@ package resolvers
import (
"context"
"fmt"
"strings"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -27,13 +28,15 @@ func Profile(ctx context.Context) (*model.User, error) {
return res, err
}
sessionToken := session.GetToken(claim.ID)
userID := fmt.Sprintf("%v", claim["id"])
email := fmt.Sprintf("%v", claim["email"])
sessionToken := session.GetToken(userID)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)
}
user, err := db.Mgr.GetUserByEmail(claim.Email)
user, err := db.Mgr.GetUserByEmail(email)
if err != nil {
return res, err
}
@@ -48,6 +51,7 @@ func Profile(ctx context.Context) (*model.User, error) {
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}

View File

@@ -35,6 +35,20 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
return res, fmt.Errorf(`invalid email address`)
}
inputRoles := []string{}
if params.Roles != nil && len(params.Roles) > 0 {
// check if roles exists
for _, item := range params.Roles {
inputRoles = append(inputRoles, *item)
}
if !utils.IsValidRolesArray(inputRoles) {
return res, fmt.Errorf(`invalid roles`)
}
} else {
inputRoles = []string{constants.DEFAULT_ROLE}
}
// find user with email
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
if err != nil {
@@ -49,6 +63,8 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
Email: params.Email,
}
user.Roles = strings.Join(inputRoles, ",")
password, _ := utils.HashPassword(params.Password)
user.Password = password
@@ -77,6 +93,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}
@@ -106,15 +123,9 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
}
} else {
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
session.SetToken(userIdStr, refreshToken)
res = &model.AuthResponse{

View File

@@ -3,8 +3,10 @@ package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -12,7 +14,7 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func Token(ctx context.Context) (*model.AuthResponse, error) {
func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx)
@@ -25,13 +27,19 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
}
claim, accessTokenErr := utils.VerifyAuthToken(token)
expiresAt := claim.ExpiresAt
expiresAt := claim["exp"].(int64)
email := fmt.Sprintf("%v", claim["email"])
user, err := db.Mgr.GetUserByEmail(claim.Email)
claimRole := fmt.Sprintf("%v", claim[constants.JWT_ROLE_CLAIM])
user, err := db.Mgr.GetUserByEmail(email)
if err != nil {
return res, err
}
if role != nil && role != &claimRole {
return res, fmt.Errorf(`unauthorized. invalid role for a given token`)
}
userIdStr := fmt.Sprintf("%v", user.ID)
sessionToken := session.GetToken(userIdStr)
@@ -46,10 +54,7 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
// if access token has expired and refresh/session token is valid
// generate new accessToken
token, expiresAt, _ = utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRole)
}
utils.SetCookie(gc, token)
res = &model.AuthResponse{
@@ -62,6 +67,7 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
},

View File

@@ -32,7 +32,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
return res, err
}
sessionToken := session.GetToken(claim.ID)
id := fmt.Sprintf("%v", claim["id"])
sessionToken := session.GetToken(id)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)
@@ -43,7 +44,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
return res, fmt.Errorf("please enter atleast one param to update")
}
user, err := db.Mgr.GetUserByEmail(claim.Email)
email := fmt.Sprintf("%v", claim["email"])
user, err := db.Mgr.GetUserByEmail(email)
if err != nil {
return res, err
}
@@ -120,9 +122,33 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
go func() {
utils.SendVerificationMail(newEmail, token)
}()
}
// TODO this idea needs to be verified otherwise every user can make themselves super admin
// rolesToSave := ""
// if params.Roles != nil && len(params.Roles) > 0 {
// currentRoles := strings.Split(user.Roles, ",")
// inputRoles := []string{}
// for _, item := range params.Roles {
// inputRoles = append(inputRoles, *item)
// }
// if !utils.IsValidRolesArray(inputRoles) {
// return res, fmt.Errorf("invalid list of roles")
// }
// if !utils.IsStringArrayEqual(inputRoles, currentRoles) {
// rolesToSave = strings.Join(inputRoles, ",")
// }
// session.DeleteToken(fmt.Sprintf("%v", user.ID))
// utils.DeleteCookie(gc)
// }
// if rolesToSave != "" {
// user.Roles = rolesToSave
// }
_, err = db.Mgr.UpdateUser(user)
if err != nil {
log.Println("Error updating user:", err)

View File

@@ -3,8 +3,10 @@ package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -41,15 +43,9 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
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, constants.DEFAULT_ROLE)
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
session.SetToken(userIdStr, refreshToken)
@@ -65,6 +61,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
},