This commit is contained in:
2024-01-22 13:48:09 +03:00
10 changed files with 638 additions and 76 deletions

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"reflect"
"strings"
"time"
log "github.com/sirupsen/logrus"
@@ -93,6 +94,53 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) {
}
}
// updateRoles will update DB for user roles, if a role is deleted by admin
// then this function will those roles from user roles if exists
func updateRoles(ctx context.Context, deletedRoles []string) error {
data, err := db.Provider.ListUsers(ctx, &model.Pagination{
Limit: 1,
Offset: 1,
})
if err != nil {
return err
}
allData, err := db.Provider.ListUsers(ctx, &model.Pagination{
Limit: data.Pagination.Total,
})
if err != nil {
return err
}
chunkSize := 1000
totalUsers := len(allData.Users)
for start := 0; start < totalUsers; start += chunkSize {
end := start + chunkSize
if end > totalUsers {
end = totalUsers
}
chunkUsers := allData.Users[start:end]
for i := range chunkUsers {
roles := utils.DeleteFromArray(chunkUsers[i].Roles, deletedRoles)
if len(chunkUsers[i].Roles) != len(roles) {
updatedValues := map[string]interface{}{
"roles": strings.Join(roles, ","),
"updated_at": time.Now().Unix(),
}
id := []string{chunkUsers[i].ID}
err = db.Provider.UpdateUsers(ctx, updatedValues, id)
if err != nil {
return err
}
}
}
}
return nil
}
// UpdateEnvResolver is a resolver for update config mutation
// This is admin only mutation
func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model.Response, error) {
@@ -291,28 +339,41 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
}, nil)
}
previousRoles := strings.Split(currentData[constants.EnvKeyRoles].(string), ",")
previousProtectedRoles := strings.Split(currentData[constants.EnvKeyProtectedRoles].(string), ",")
updatedRoles := strings.Split(updatedData[constants.EnvKeyRoles].(string), ",")
updatedDefaultRoles := strings.Split(updatedData[constants.EnvKeyDefaultRoles].(string), ",")
updatedProtectedRoles := strings.Split(updatedData[constants.EnvKeyProtectedRoles].(string), ",")
// check the roles change
if len(params.Roles) > 0 {
if len(params.DefaultRoles) > 0 {
// should be subset of roles
for _, role := range params.DefaultRoles {
if !utils.StringSliceContains(params.Roles, role) {
log.Debug("Default roles should be subset of roles")
return res, fmt.Errorf("default role %s is not in roles", role)
}
if len(updatedRoles) > 0 && len(updatedDefaultRoles) > 0 {
// should be subset of roles
for _, role := range updatedDefaultRoles {
if !utils.StringSliceContains(updatedRoles, role) {
log.Debug("Default roles should be subset of roles")
return res, fmt.Errorf("default role %s is not in roles", role)
}
}
}
if len(params.ProtectedRoles) > 0 {
for _, role := range params.ProtectedRoles {
if utils.StringSliceContains(params.Roles, role) || utils.StringSliceContains(params.DefaultRoles, role) {
if len(updatedProtectedRoles) > 0 {
for _, role := range updatedProtectedRoles {
if utils.StringSliceContains(updatedRoles, role) || utils.StringSliceContains(updatedDefaultRoles, role) {
log.Debug("Protected roles should not be in roles or default roles")
return res, fmt.Errorf("protected role %s found roles or default roles", role)
}
}
}
deletedRoles := utils.FindDeletedValues(previousRoles, updatedRoles)
if len(deletedRoles) > 0 {
go updateRoles(ctx, deletedRoles)
}
deletedProtectedRoles := utils.FindDeletedValues(previousProtectedRoles, updatedProtectedRoles)
if len(deletedProtectedRoles) > 0 {
go updateRoles(ctx, deletedProtectedRoles)
}
// Update local store
memorystore.Provider.UpdateEnvStore(updatedData)
jwk, err := crypto.GenerateJWKBasedOnEnv()

View File

@@ -9,6 +9,7 @@ import (
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/authenticators"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
@@ -60,6 +61,66 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
return res, err
}
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
if err != nil || !isMFADisabled {
log.Debug("MFA service not enabled: ", err)
}
isTOTPLoginDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
if err != nil || !isTOTPLoginDisabled {
log.Debug("totp service not enabled: ", err)
}
setOTPMFaSession := func(expiresAt int64) error {
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return err
}
cookie.SetMfaSession(gc, mfaSession)
return nil
}
// If mfa enabled and also totp enabled
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isTOTPLoginDisabled {
expiresAt := time.Now().Add(3 * time.Minute).Unix()
if err := setOTPMFaSession(expiresAt); err != nil {
log.Debug("Failed to set mfa session: ", err)
return nil, err
}
authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator)
if err != nil || authenticator == nil || authenticator.VerifiedAt == nil {
// generate totp
// Generate a base64 URL and initiate the registration for TOTP
authConfig, err := authenticators.Provider.Generate(ctx, user.ID)
if err != nil {
log.Debug("error while generating base64 url: ", err)
return nil, err
}
recoveryCodes := []*string{}
for _, code := range authConfig.RecoveryCodes {
recoveryCodes = append(recoveryCodes, refs.NewStringRef(code))
}
// when user is first time registering for totp
res = &model.AuthResponse{
Message: `Proceed to totp verification screen`,
ShouldShowTotpScreen: refs.NewBoolRef(true),
AuthenticatorScannerImage: refs.NewStringRef(authConfig.ScannerImage),
AuthenticatorSecret: refs.NewStringRef(authConfig.Secret),
AuthenticatorRecoveryCodes: recoveryCodes,
}
return res, nil
} else {
//when user is already register for totp
res = &model.AuthResponse{
Message: `Proceed to totp screen`,
ShouldShowTotpScreen: refs.NewBoolRef(true),
}
return res, nil
}
}
isSignUp := false
if user.EmailVerifiedAt == nil {
isSignUp = true