Skip to content
Open
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
81964c7
Refactoring and adding shoreline models to platform/user for auth.
lostlevels Apr 16, 2026
6cfa1b2
Cleanup.
lostlevels Feb 7, 2024
5e4e602
Add /v1/profiles/:userId route.
lostlevels Apr 16, 2026
6135115
Renaming.
lostlevels Feb 12, 2024
d159e8c
Add the route.
lostlevels Apr 16, 2026
ee311c4
Fix build.
lostlevels Feb 12, 2024
962ebc1
Fix test.
lostlevels Apr 16, 2026
a746294
Use permissions like in seagull.
lostlevels Apr 16, 2026
de33718
Validate the profile.
lostlevels Feb 15, 2024
52fb052
HasWritePermissions.
lostlevels Feb 21, 2024
51706a3
Rename profile routes to be consistent w/ existing ones.
lostlevels Mar 17, 2024
93222a3
Use snakecase attributes for now but don't flatten yet as blip is still
lostlevels Mar 27, 2024
1c6eba9
Have a LegacyUserProfile to support seagull requests.
lostlevels Apr 1, 2024
16f00c9
Change leagcy profile routes for simpler proxying in routetable.
lostlevels Apr 1, 2024
d8173c2
Add legacy delete route.
lostlevels Apr 1, 2024
2f46dac
Move keycloak client and keycloak user_accessor to own package.
lostlevels Apr 1, 2024
72d9ebf
Rename to package keycloak.
lostlevels Apr 16, 2026
50d0815
Add user profile config for keycloak 24+.
lostlevels Apr 1, 2024
42420ce
Remove user profile config as that's handled in TF.
lostlevels Apr 2, 2024
7898935
Add custodian field to profile.
lostlevels Apr 2, 2024
1edbbfc
Allow services to retrieve user profile.
lostlevels Apr 11, 2024
b4d7e7e
Use "dummy" attribute "profile_has_custodian" for easier keycloak
lostlevels Apr 16, 2024
c95095a
patient.fullName is only set for fake children.
lostlevels Apr 19, 2024
7fd9db5
Remove "profile_" prefix from profile keycloak attributes. Add
lostlevels Apr 23, 2024
df42975
Use right json.
lostlevels Apr 29, 2024
0167a45
Delete unused shoreline code. Move user.FullUser into user.User.
lostlevels Apr 16, 2026
bc83f89
Remove unused hasher code.
lostlevels Apr 30, 2024
a4977b5
Remove unused fields.
lostlevels Apr 30, 2024
70c0a29
Add MRN attribute.
lostlevels Apr 30, 2024
80ad018
Copy amoeba's permissions with regards to membership and custodian.
lostlevels May 1, 2024
551db76
Remove check from route since part of middleware now.
lostlevels May 1, 2024
fa04272
Remove unneeded comment.
lostlevels May 29, 2024
569050a
Add GroupsForUser as a prelude to some seagull / gatekeeper
lostlevels Apr 16, 2026
0c4f85e
Commence "old" seagull routes that retrieves from the seagull collection
lostlevels Jun 5, 2024
220183a
Update migration status.
lostlevels Jun 5, 2024
0d1a9da
Make sure seagull.value field is preserved properly during updates and
lostlevels Jun 7, 2024
cbab818
Use fallback profile accessor to check for profile first in seagull.
lostlevels Apr 16, 2026
fb44208
Rename repository for clarity of purpose.
lostlevels Jun 11, 2024
5eab377
Bump gocloak.
lostlevels Apr 16, 2026
7cf807f
role field.
lostlevels Jun 19, 2024
ae63ba1
Omit profile fields if empty in response.
lostlevels Jun 20, 2024
742c06a
Add clinic profile fields.
lostlevels Jun 24, 2024
8378b34
Add normalizer methods for profiles.
lostlevels Jun 25, 2024
ad31dcd
Account for empty profile fullName.
lostlevels Jul 8, 2024
3265f74
[BACK-3046] Create initial shared users with profiles path w/o
lostlevels Apr 16, 2026
3fca9bb
Start metadata/users/:userid/users filter params.
lostlevels Jul 10, 2024
9a27e5b
Parse users profiles query filter.
lostlevels Jul 10, 2024
1ceb4b9
Update users route to properly filter out users. Document Permission /
lostlevels Apr 16, 2026
62db0a4
Remove unused query filter on users profiles.
lostlevels Jul 17, 2024
d58642c
Handle email and emails in legacy seagull profiles.
lostlevels Jul 30, 2024
e1de168
Read raw value as map from seagull value.
lostlevels Jul 31, 2024
9fc1750
Allow setting of profile on seagull document's value field.
lostlevels Jul 31, 2024
94d0a00
Migrate diagnosisType.
lostlevels Jul 31, 2024
a79c2cd
Remove email field from clinic as confirmed only a few fake clinic pr…
lostlevels Aug 6, 2024
20e6964
Use correct FullName in case of fake children.
lostlevels Aug 7, 2024
374d117
Handle certain incorrect types in legacy seagull profile.
lostlevels Aug 8, 2024
9eb119e
Fix some logic and tests for profiles.
lostlevels Apr 16, 2026
d643c07
Set max profile field length to equal keycloak < 24
lostlevels Aug 12, 2024
8737671
Make some profile values pointers so that some legacy migration profiles
lostlevels Aug 14, 2024
32682d6
Export MaxProfileFieldLen
lostlevels Aug 14, 2024
d11dc0a
Remove unused field, add tests, synchronize keycloak access.
lostlevels Apr 16, 2026
871f707
Use existing UsersArray type, add update tests.
lostlevels Aug 15, 2024
8c775e5
Make TargetTimezone a value.
lostlevels Apr 17, 2026
bc4b082
Remove unused files, remove unneded checks.
lostlevels Apr 17, 2026
e425e62
Fix tests broken during rebase.
lostlevels Apr 20, 2026
97daee8
Regenerate.
lostlevels Apr 20, 2026
8dbf5a7
Add same named routes as seagull for clients that connect directly to…
lostlevels Apr 29, 2026
f91d213
Fix shadowed variable profile overwriting update.
lostlevels Apr 29, 2026
b4d70a9
Add /v1/users/:userId/profile route tests.
lostlevels May 19, 2026
c2137d6
Updates from code review.
lostlevels Jun 11, 2026
7445261
Update tests.
lostlevels Jun 11, 2026
ea76335
Use custodian / root permission fors /users route. Update tests
lostlevels Jun 11, 2026
1dc89ac
Updates from code review. Remove unused fields, fix permissions logic.
lostlevels Jun 17, 2026
f4955c2
run imports
lostlevels Jun 17, 2026
4437749
Some updates from PR comments.
lostlevels Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions appvalidate/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions auth/service/api/v1/appvalidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,29 @@ var _ = Describe("App Validation", func() {
challenge := "challenge"
serverSessionToken := "serverToken"

unattestedUser := user{
unattestedUser := testUser{
UserID: "unattested",
SessionToken: "unattestedToken",
Details: request.NewAuthDetails(request.MethodSessionToken, "unattested", "unattestedToken"),
AttestationVerified: false,
}
attestedUser := user{
attestedUser := testUser{
UserID: "attested",
SessionToken: "attestedToken",
Details: request.NewAuthDetails(request.MethodSessionToken, "attested", "attestedToken"),
KeyID: "YWJjZGVmYWJjZGVm",
AttestationVerified: false,
AttestationChallenge: challenge,
}
attestedUnverifiedUser := user{
attestedUnverifiedUser := testUser{
UserID: "attestedUnverified",
SessionToken: "attestedUnverifiedToken",
Details: request.NewAuthDetails(request.MethodSessionToken, "attestedUnverified", "attestedUnverified"),
KeyID: "YWRzZmFkZg==",
AttestationVerified: false,
AttestationChallenge: challenge,
}
attestedVerifiedUser := user{
attestedVerifiedUser := testUser{
UserID: "attestedVerified",
SessionToken: "attestedVerifiedToken",
Details: request.NewAuthDetails(request.MethodSessionToken, "attestedVerified", "attestedVerifiedToken"),
Expand All @@ -75,7 +75,7 @@ var _ = Describe("App Validation", func() {
AttestationChallenge: challenge,
AssertionChallenge: challenge,
}
users := []user{
users := []testUser{
unattestedUser,
attestedUser,
attestedVerifiedUser,
Expand Down Expand Up @@ -334,7 +334,7 @@ var _ = Describe("App Validation", func() {
})

// user is a helper user that contains relevant user information for tests.
type user struct {
type testUser struct {
UserID string
SessionToken string
Details request.AuthDetails
Expand Down
116 changes: 116 additions & 0 deletions auth/service/api/v1/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package v1

import (
"net/http"

"github.com/ant0ine/go-json-rest/rest"

"github.com/tidepool-org/platform/request"
)

// requireCustodian aborts with an error if the user associated w/ the
// request doesn't have custodian access to the user with the id defined in the
// url param targetParamUserID.
//
// This mimics the logic of amoeba's requireCustodian access. This means a
// user has access to the target user if any of the following is true:
// - The is a service call (AuthDetails.IsService() == true)
// - The requester and target are the same - AuthDetails.UserID == targetParamUserID
// - The requester has explicit permissions to access targetParamUserID
func (r *Router) requireCustodian(targetParamUserID string, handlerFunc rest.HandlerFunc) rest.HandlerFunc {
return func(res rest.ResponseWriter, req *rest.Request) {
if handlerFunc != nil && res != nil && req != nil {
targetUserID := req.PathParam(targetParamUserID)
responder := request.MustNewResponder(res, req)
ctx := req.Context()
details := request.GetAuthDetails(ctx)
if details == nil {
request.MustNewResponder(res, req).Error(http.StatusUnauthorized, request.ErrorUnauthenticated())
return
}
if details.IsService() || details.UserID() == targetUserID {
handlerFunc(res, req)
return
}
hasPerms, err := r.PermissionsClient().HasCustodianPermissions(ctx, details.UserID(), targetUserID)
if err != nil {
responder.InternalServerError(err)
return
}
if !hasPerms {
responder.Empty(http.StatusForbidden)
return
}
handlerFunc(res, req)
}
}
}

// requireMembership proceeds if the user with the id specified in the URL
// paramter targetParamUserID has some association with the user in the current
// request - the "requester". This mimics amoeba's requireMembership function.
//
// This proceeds if any of the following are true:
// - The is a service call (AuthDetails.IsService() == true)
// - The requester and target are the same - AuthDetails.UserID == targetParamUserID
// - The requester has any permissions to targetParamUserID OR targetParamUserID has permissions to the requester.
func (r *Router) requireMembership(targetParamUserID string, handlerFunc rest.HandlerFunc) rest.HandlerFunc {
return func(res rest.ResponseWriter, req *rest.Request) {
if handlerFunc != nil && res != nil && req != nil {
targetUserID := req.PathParam(targetParamUserID)
responder := request.MustNewResponder(res, req)
ctx := req.Context()
details := request.GetAuthDetails(ctx)
if details == nil {
request.MustNewResponder(res, req).Error(http.StatusUnauthorized, request.ErrorUnauthenticated())
return
}
if details.IsService() || details.UserID() == targetUserID {
handlerFunc(res, req)
return
}
hasPerms, err := r.PermissionsClient().HasMembershipRelationship(ctx, details.UserID(), targetUserID)
if err != nil {
responder.InternalServerError(err)
return
}
if !hasPerms {
responder.Empty(http.StatusForbidden)
return
}
handlerFunc(res, req)
}
}
}

// requireWriteAccess aborts with an error if the request isn't a server request
// or the authenticated user doesn't have access to the user id in the url param,
// targetParamUserID
func (r *Router) requireWriteAccess(targetParamUserID string, handlerFunc rest.HandlerFunc) rest.HandlerFunc {
Comment thread
lostlevels marked this conversation as resolved.
Outdated
return func(res rest.ResponseWriter, req *rest.Request) {
if handlerFunc != nil && res != nil && req != nil {
targetUserID := req.PathParam(targetParamUserID)
responder := request.MustNewResponder(res, req)
ctx := req.Context()
details := request.GetAuthDetails(ctx)
if details == nil {
responder.Empty(http.StatusUnauthorized)
return
}
if details.IsService() {
handlerFunc(res, req)
return
}
hasPerms, err := r.PermissionsClient().HasWritePermissions(ctx, details.UserID(), targetUserID)
if err != nil {
responder.InternalServerError(err)
return
}
if !hasPerms {
responder.Empty(http.StatusForbidden)
return
}
handlerFunc(res, req)
}
}
}
Loading
Loading