-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SDP-1247] new API role for bypassing reCAPTCHA #368
Conversation
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
Something went wrong with PR preview build please check |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
stellar-disbursement-platform-backend-preview is available here: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on that.
I still found a few issues. Comments below ⬇️
// NOTE: in the MVP, users should have only one role. | ||
validator.Check(len(roles) == 1, "roles", "the number of roles required is exactly one") | ||
|
||
validator.Check(len(roles) >= 1, "roles", "the number of roles required is greater than or equal to 1") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also be validating that the user will not end up with just the API role.
user, err := h.AuthManager.GetUserByEmail(ctx, strings.TrimSpace(forgotPasswordRequest.Email)) | ||
if err != nil { | ||
// If user cannot be found, we will default to user requiring to validate reCAPTCHA | ||
if errors.Is(err, auth.ErrUserNotFound) { | ||
// If we don't find the user by email, we just return an ok response | ||
// to prevent malicious client from searching accounts in the system | ||
log.Ctx(ctx).Errorf("Email in request not found: %s", utils.TruncateString(forgotPasswordRequest.Email, 3)) | ||
} | ||
} | ||
|
||
userRoleCanBypassReCAPTCHA := false | ||
if user != nil { | ||
userRoleCanBypassReCAPTCHA = slices.Contains(user.Roles, data.APIUserRole.String()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 since we don't care much about this error, we could simplify this logic a bit:
user, err := h.AuthManager.GetUserByEmail(ctx, strings.TrimSpace(forgotPasswordRequest.Email)) | |
if err != nil { | |
// If user cannot be found, we will default to user requiring to validate reCAPTCHA | |
if errors.Is(err, auth.ErrUserNotFound) { | |
// If we don't find the user by email, we just return an ok response | |
// to prevent malicious client from searching accounts in the system | |
log.Ctx(ctx).Errorf("Email in request not found: %s", utils.TruncateString(forgotPasswordRequest.Email, 3)) | |
} | |
} | |
userRoleCanBypassReCAPTCHA := false | |
if user != nil { | |
userRoleCanBypassReCAPTCHA = slices.Contains(user.Roles, data.APIUserRole.String()) | |
} | |
userRoleCanBypassReCAPTCHA := false | |
user, err := h.AuthManager.GetUserByEmail(ctx, strings.TrimSpace(forgotPasswordRequest.Email)) | |
if err == nil { | |
userRoleCanBypassReCAPTCHA = slices.Contains(user.Roles, data.APIUserRole.String()) | |
} |
resetToken, err := h.AuthManager.ForgotPassword(ctx, strings.TrimSpace(forgotPasswordRequest.Email)) | ||
// If we don't find the user by email, we just return an ok response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the second time you'te triming the space. Why don't you do it once, right at the beginning, by updating the value in forgotPasswordRequest.Email
with it's trimmed version?
u, err := authenticator.GetUserByEmail(ctx, randUser.Email) | ||
require.NoError(t, err) | ||
|
||
assert.Equal(t, randUser.ID, u.ID) | ||
assert.Equal(t, randUser.Email, u.Email) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks for explaining it.
Once() | ||
for _, userRoles := range usersRoles { | ||
targetUser := &auth.User{ | ||
ID: "user-ID", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few lines above you used userID
and here you have user-ID
. Is that a typo? Is that field being used for anything? Should we add a test for it?
t.Run("Should return http status 400 when attempting to validate reCAPTCHA token after failing to find user by email", func(t *testing.T) { | ||
requestBody := ` | ||
{ | ||
"email": "[email protected]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be a better naming? WDYT?
"email": "valid@email.com" | |
"email": "not-found@email.com" |
// If user cannot be found, we will default to user requiring to validate reCAPTCHA | ||
if errors.Is(err, auth.ErrUserNotFound) { | ||
// If we don't find the user by email, we just return an ok response | ||
// to prevent malicious client from searching accounts in the system | ||
log.Ctx(ctx).Errorf("Email in request not found: %s", utils.TruncateString(reqBody.Email, 3)) | ||
} | ||
} | ||
|
||
userRoleCanBypassReCAPTCHA := false | ||
if user != nil { | ||
userRoleCanBypassReCAPTCHA = slices.Contains(user.Roles, data.APIUserRole.String()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here about simplifying this statement.
defaultReqBody := fmt.Sprintf( | ||
`{"email": "%s", "password": "%s", "recaptcha_token": "%s"}`, email, password, defaultReCAPTCHAToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the naming here can be improved for better clarity. I mentioned this in a previous review on this same PR, but what does default mean vs non-default? If this is meant to be a login request body with or without recaptcha, you should improve make sure the const tame conveys that meaning.
func authenticateSetup( | ||
t *testing.T, | ||
authenticatorMock *auth.AuthenticatorMock, | ||
jwtManagerMock *auth.JWTManagerMock, | ||
user *auth.User, password, userToken string, | ||
) { | ||
t.Helper() | ||
authenticatorMock. | ||
On("ValidateCredentials", mock.Anything, user.Email, password). | ||
Return(user, nil). | ||
Once() | ||
jwtManagerMock. | ||
On("GenerateToken", mock.Anything, user, mock.AnythingOfType("time.Time")). | ||
Return(userToken, nil). | ||
Once() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you be more specific on this method name? Maybe prepareMocksForLogin
would be more accurate?
return | ||
} | ||
|
||
user, err := h.AuthManager.GetUserByDeviceID(ctx, strings.TrimSpace(deviceID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 It doesn't look like the implementation of GetUserByDeviceID
is returning the user roles.
What
Completed:
APIUserRole
that could be concatenated with existing user roles to override reCAPTCHA validation/login
,/mfa
, and/forgot-password
that would usually require reCAPTCHA validation normally##Tests
The following calls were made referring to users with the
api
role and without the recaptcha token in the request body/login
request
response
/mfa
request
response
/forgot-password
request
response
Why
[TODO: Why this change is being made. Include any context required to understand the why.]
Known limitations
[TODO or N/A]
Checklist
PR Structure
Thoroughness
Configs and Secrets
values.yaml
file.pr-preview
,dev
,demo
,prd
).values.yaml
file.pr-preview secrets
,dev secrets
,demo secrets
,prd secrets
).Release
develop
ormain
after it's ready for production!Deployment