...
This makes it difficult to talk about changes to the management of accounts, even for something like authorization. Here is one aspirational model where we separate Synapse-managed accounts and participant accounts, so that all the business logic around participants is separated from our code to manage admin accounts. Basically “Account
" would refer to an administrative account and "Participant" would refer to a participant account most places in our code. (An alternative is to create a new thing, like AdminAccount
, for the admin accounts):
...
There would be backwards-incompatible consequences to this refactor. The /v3/participants APIs could no longer be used to create and manage administrative accounts. I don’t know who uses these for that purpose, at least the Bridge Study Manage would need to revise the UI it has to list all admins/users in one giant list (currently under the legacy panel). That API is still useful, but it would only return true participant accounts.
...
Explicitly mark administrative accounts before we migrate roles, because right now that's the way we determine these kind of accounts. We don’t want administrative accounts to turn into participant accounts if someone (by mistake or otherwise) removes the roles from the account.
AccountDao & AccountSecretDao
Basically fine as is. AccountDao.getAppIdForUser is weird. AccountDao.getPagedExternalIds is weird but probably legacyIn the refactor above, a number of services can directly use AccountDao rather than AccountService. AccountDao and AccountSecretDao are basically fine as is, but we could write faster custom queries to return things like ID and health code than we are currently doing in the service classes.
ParticipantVersionService
Should monitor changes to the Accounts table that are done through ParticipantService, not AccountService.
ConsentService, EnrollmentService
...
AccountService & ParticipantService
Could use an abstract parent. e.g. do you call "getAccountId" on account service or participant service? Maybe both is OK. Deleting an account: lots of common code between deleteAccount() and deleteParticipant.These two could use a common abstract parent class. For example, deleteAccount() and deleteParticipant() would share a lot of code in common. Other places they will be pretty different (e.g. only AccountService needs to enforce role change logic, only participant service needs to deal with enrollment scenarios, etc.)
AccountService
move authenticate, changePassword, reauthenticate, deleteReauthToken, verifyChannel to the AuthenticationService.
ActivityEventService: Event services should not be used in ParticipantServiceStudyActivityEventService: AccountService, they should be used in ParticipantService;
ParticipantVersionService: should be used in ParticipantVersionService
It does seem like simple methods to get the ID of an account or the like could continue to use AccountService.any attempt to manipulate participant accounts through this service should fail immediately with a bad request exception;
ParticipantService
AccountService -> AccountDao
Any code related to roles and/or organizations can be removed to AccountService;
any attempt to manipulate an admin account accounts through this service should fail fastimmediately with a bad request exception;
updateIdentifiers: authenticate in the controller as part of the security check so you can take these auth methods out of AccountService and ParticipantService. Also this method is more likely useful on AccountService (arguably we can just allow administrators to change participant’s phone and email address).
Auth: AuthenticationService, AccountWorkflowService, OAuthProviderService
These are all related and could be combined, except that it would make an unwieldy service. Maybe we could have a front controller that delegates to a set of implementation classes so external classes only deal with an AuthenticationService, but we're not maintaining a monolith.
AuthenticationService should drop reference to AccountService and ParticipantService and work only with AccountDao and AccountSecretDaoAccountWorkflowService should drop reference to AccountService and use AccountDao./ParticipantService → AccountDao in all three services
UserAdminService
Fine as is (a crazy service to make integration testing palatable).
AccountController
add paginated API to retrieve admin accounts in the app (across all organizations or no organiationsorganizations…organizations have a membership API that most of our external users should be using);
access to these methods would require app:admin or system:superadmin powers ;all methods should only operate on admin accounts(see below);
UserManagementController
it may be simpler and easier to create enrollment records directly in the account for testing purposes. This would remove the consent service and the enrollment service references;
the account service user delete should delete from all those other services, so we don't have to reference userAdminService to properly delete an account.
...
We’re seeking a permissions model that will cover our current security capabilities while tackling new use cases, such as the ability to manage access to studies.
The permission model will be permission-based, assigning a role vis-a-vis a specific target model (aka a “grant”);
These new permissions will still be assigned to a user (not their assignment to an organization; this has important consequences for managing roles);
Organizations will principally be a means to communicate who can see whom in a multi-tenanted application. Accounts will be assignable to multiple organizations. Migrated accounts will be given permissions to the sponsored studies of that organization, and then going forward, a user will at least have the auditor role for all studies sponsored by all of their organizations (this is the only transitively assigned role we’ve identified at this time);
Bridge roles are hierarchical. Generally a user should have only one role vis-a-vis a model object. For example, being a study designer implies that you can read information about studies, like the auditor role. Every study designer does not also have to be an auditor.
Currently an app-scoped developer, researcher, or admin can operate on any model in the app that requires that role (or its scoped counterpart, like study designer). WHAT SHALL WE DO ABOUT THIS?
Use Cases
Use Case | |
---|---|
Permissions changes should register for users without them having to sign out and sign back in again | (if If cached they need to be separate from the session )in Redis. Otherwise, reading them on each request would meet this requirement. |
New admin account created with a sandbox in which studies can be created/edited that are not visible to others | When an account creates a study, it will be made the admin of that study. Searching for lists of studies will only return studies for which the caller has a role (which might be transitively assigned through an organization, effectively saying that organization membership grants visibility to studies as well). |
“Sandbox” can be converted to real study, with additional users in specific roles for that study | Admin of a study can add additional users. We have not specified how we will make a study an “evaluation” study but that would need to be removable. |
Study is extended by creating a new study | Admin of the new study would need to copy over all the permissions from the old study. Bridge’s APIs should make this straightforward to do. |
Add someone to a study’s administration team | Add a permission (a role vis-a-vis the study) to that study. |
Remove someone from a study’s administration team | Remove a permission (a role vis-a-vis the study) from that study. |
Create similar authorization model for assessments | We should be able to expand it this approach to other things than studies, because it seems likely we’ll encounter something else that needs finer-grained authorizationany other model object we want to secure. |
Secured objects/scopes
Organizations
“Teams” in Synapse impart an identical set of permissions to a project for a set of users. “Organizations” in Bridge are a scope for manipulating users, since our app is multi-tenanted. The roles related to organizations:
Role
Scope
Administrator
Organization
Model | Role | |
---|---|---|
Organization | Administrator |
|
Organization | Member |
Organization
|
...
Studies
...
|
...
Roles
Scope
Developer
Study
| ||
Study | Auditor | Can read information about the study and its schedule. Cannot edit anything and can’t see people. |
Study | Developer | Can read and edit the configuration of the study and its schedule. |
Study | Researcher |
Can list, view, edit, delete, enroll and unenroll accounts in the study. | |
Study | PI_Agent |
Can move the study into a production stage, and can view the data being exported to a project in Synapse. User must be a validated Synapse user. Can delete a study. | |
Study | Admin |
Can do anything related to a study or its participants, and they can change users associated to the study or their roles in that association. | ||
App | Developer | Can access many APIs for the configuration of an app and related resources like app configs. |
App | Researcher | Can see all accounts in the system regardless of organization or study boundaries. |
App | Admin | Can call any API in the scope of the account’s app. |
Global | Worker | Can access APIs that specifically allow the worker to call across app boundaries without switching applications first. |
Global | Admin | Can do anything in any app, study, or organization (superadmin) |
Note that membership in an organization is also directly modeled in the database right now via the Account.orgMembership field, and will be moved to an associative table. We may not need a “member” role though it may be more convenient.
App/System
These are legacy roles but there are still many APIs that require one of these roles, particularly around configuring an app:
...