UpdatePrincipal updates or creates a principal from a PrincipalConfig structure.
(ctx context.Context, updates *auth.PrincipalConfig, isUser bool, allowReplace bool)
| 43 | |
| 44 | // UpdatePrincipal updates or creates a principal from a PrincipalConfig structure. |
| 45 | func (dbc *DatabaseContext) UpdatePrincipal(ctx context.Context, updates *auth.PrincipalConfig, isUser bool, allowReplace bool) (replaced bool, princ auth.Principal, err error) { |
| 46 | // Sanity checking |
| 47 | if !base.AllOrNoneNil(updates.JWTIssuer, updates.JWTRoles, updates.JWTChannels) { |
| 48 | return false, princ, fmt.Errorf("must either specify all OIDC properties or none") |
| 49 | } |
| 50 | |
| 51 | // Get the existing principal, or if this is a POST make sure there isn't one: |
| 52 | var user auth.User |
| 53 | authenticator := dbc.Authenticator(ctx) |
| 54 | |
| 55 | // Retry handling for cas failure during principal update. Limiting retry attempts |
| 56 | // to PrincipalUpdateMaxCasRetries defensively to avoid unexpected retry loops. |
| 57 | for i := 1; i <= auth.PrincipalUpdateMaxCasRetries; i++ { |
| 58 | if isUser { |
| 59 | user, err = authenticator.GetUser(*updates.Name) |
| 60 | princ = user |
| 61 | } else { |
| 62 | princ, err = authenticator.GetRole(*updates.Name) |
| 63 | } |
| 64 | if err != nil { |
| 65 | return replaced, princ, err |
| 66 | } |
| 67 | |
| 68 | changed := false |
| 69 | replaced = (princ != nil) |
| 70 | if !replaced { |
| 71 | if updates.Name == nil || *updates.Name == "" { |
| 72 | return replaced, princ, fmt.Errorf("UpdatePrincipal: cannot create principal with empty name") |
| 73 | } |
| 74 | // If user/role didn't exist already, instantiate a new one: |
| 75 | if isUser { |
| 76 | isValid, reason := updates.IsPasswordValid(dbc.AllowEmptyPassword) |
| 77 | if !isValid { |
| 78 | err = base.HTTPErrorf(http.StatusBadRequest, "Error creating user: %s", reason) |
| 79 | return replaced, princ, err |
| 80 | } |
| 81 | user, err = authenticator.NewUserNoChannels(*updates.Name, "") |
| 82 | princ = user |
| 83 | } else { |
| 84 | princ, err = authenticator.NewRoleNoChannels(*updates.Name) |
| 85 | } |
| 86 | if err != nil { |
| 87 | return replaced, princ, fmt.Errorf("Error creating user/role: %w", err) |
| 88 | } |
| 89 | princ.SetCreatedAt(time.Now().UTC()) |
| 90 | changed = true |
| 91 | } else if !allowReplace { |
| 92 | err = base.HTTPErrorf(http.StatusConflict, "Already exists") |
| 93 | return |
| 94 | } else if isUser && updates.Password != nil { |
| 95 | isValid, reason := updates.IsPasswordValid(dbc.AllowEmptyPassword) |
| 96 | if !isValid { |
| 97 | err = base.HTTPErrorf(http.StatusBadRequest, "Error updating user/role: %s", reason) |
| 98 | return replaced, princ, err |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | // Ensure the caller isn't trying to set all_channels or roles explicitly - it'll get recomputed automatically. |