-
Notifications
You must be signed in to change notification settings - Fork 118
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
Introduce Role-Based Authentication for Repository Management #1060
base: main
Are you sure you want to change the base?
Conversation
Motivation To ensure smooth compatibility with line#1060, the `PerRolePermissions` and `RepositoryMetadata` structure need an update to support changes. Modifications: - Enhanced the `PerRolePermissions` and `RepositoryMetadata` deserializers to accept both an array or permissions and a permission. - Added `REPO_ADMIN` `Permission`. - Removed `MetadataApiService.updateSpecificUserPermission` and `updateSpecificTokenPermission`. - They are not used in the UI and we don't even have test cases for that. - Will add those APIs later when we need it. Result: - The `PerRolePermissions` and `RepositoryMetadata` structures now support the upcoming changes in line#1060.
@RequiresRole(roles = { ProjectRole.OWNER, ProjectRole.MEMBER }) | ||
public CompletableFuture<ProjectMetadata> getProjectMetadata( | ||
@Param String projectName, | ||
@Param("checkPermissionOnly") @Default("false") boolean isCheckPermissionOnly) { |
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 parameter was introduced here but has not been used anywhere. Let me just remove that.
// } | ||
// } | ||
// } | ||
final JsonPointer path = JsonPointer.compile("/repos" + encodeSegment(repoName) + "/roles/projects"); |
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 understand that this code will be applied after the migration is complete.
server/src/main/java/com/linecorp/centraldogma/server/metadata/MetadataService.java
Outdated
Show resolved
Hide resolved
// } | ||
// } | ||
// } | ||
final JsonPointer path = JsonPointer.compile("/repos" + encodeSegment(repoName) + "/roles/projects"); |
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.
Note: It would be nice to have a DSL/builder to build a JSON pointer type-safely. Let's consider it later.
JsonPointers
.meta()
.repos(repoName)
.roles()
.projects()
.compile();
JsonPointers
.meta()
.members(repoName)
.compile();
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.
That's a good suggestion. By the way, this PR is in WIP and I forgot to tell you about it.
Would you mind reviewing #1064, beforehand?
return repositoryMetadata.perRolePermissions().guest(); | ||
@Nullable | ||
private static RepositoryRole repositoryRole(Roles roles, @Nullable RepositoryRole repositoryRole, | ||
@Nullable ProjectRole projectRole) { |
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.
projectRole
seems not null always in the current usage.
@Nullable ProjectRole projectRole) { | |
ProjectRole projectRole) { |
if (memberOrGuestRole == null) { | ||
return repositoryRole; | ||
} | ||
if (memberOrGuestRole == RepositoryRole.WRITE || repositoryRole == RepositoryRole.WRITE) { |
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.
Let's say a-member-user
is granted to READ
role and projects.member
is granted to WRITE
. Is WRITE
given for a-member-user
?
{
"repos": {
"my-repo": {
"name": "my-repo",
"roles": {
"users": {
"a-member-user": "READ"
}
"projects": {
"member": "WRITE",
...
}
}
}
}
}
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.
Is WRITE given for a-member-user?
That is correct, as similar to how GitHub handles it.
The role with the highest authority will take precedence.
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.
That's a little surprising - I was expecting the repository role to override the project role.
If that's how Github is doing it, then I guess the current approach is fine.
server/src/main/java/com/linecorp/centraldogma/server/metadata/Roles.java
Outdated
Show resolved
Hide resolved
Motivation To ensure smooth compatibility with #1060, the `RepositoryMetadata` structure needs an update to support changes. AS-IS: ```json { "name": "minu-test", "perRolePermissions": { "owner": [ "READ", "WRITE" ], "member": ["READ"], "guest": [] }, "perUserPermissions": { "[email protected]": [ "READ" ], "[email protected]": [ "READ", "WRITE" ] }, "perTokenPermissions": { "goodman": [ "READ" ] }, "creation": { "user": "[email protected]", "timestamp": "2024-08-19T02:47:23.370762417Z" } } ``` TO-BE: ```json { "name": "minu-test", "roles": { "projects": { "member": "READ", "guest": null } "users": { "[email protected]": "READ", "[email protected]": "WRITE" }, "tokens": { "goodman": "READ" } }, "creation": { "user": "[email protected]", "timestamp": "2024-08-19T02:47:23.370762417Z" } } ``` Modifications: - Enhanced the `RepositoryMetadata` deserializers to accept both legacy and new formats. - Added `RepositoryRole`. - It will be used for repository instead of `Permission`. - Removed `MetadataApiService.updateSpecificUserPermission` and `updateSpecificTokenPermission`. - They are not used in the UI and we don't even have test cases for that. - Will add those APIs later with better API design when we need it. Result: - The `RepositoryMetadata` structure now supports the upcoming changes in #1060.
This PR is ready. PTAL. 🙇 |
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.
Apart from MetadataService#repositoryRole
, I understood that the rest of changes are simple renaming/refactoring.
return repositoryMetadata.perRolePermissions().member(); | ||
default: | ||
return repositoryMetadata.perRolePermissions().guest(); | ||
@Nullable |
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.
Optional) What do you think of adding a RepositoryRole.NONE
? It doesn't seem intuitive that a method returning an enum is nullable.
@Nullable |
if (memberOrGuestRole == null) { | ||
return repositoryRole; | ||
} | ||
if (memberOrGuestRole == RepositoryRole.WRITE || repositoryRole == RepositoryRole.WRITE) { |
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.
That's a little surprising - I was expecting the repository role to override the project role.
If that's how Github is doing it, then I guess the current approach is fine.
default: | ||
return repositoryMetadata.perRolePermissions().guest(); | ||
@Nullable | ||
private static RepositoryRole repositoryRole(Roles roles, @Nullable RepositoryRole repositoryRole, |
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.
optional) the logic in this class was not easy to follow at first glance. To me, the following flow makes more sense:
@Nullable
private static RepositoryRole repositoryRole(Roles roles, @Nullable RepositoryRole repositoryRole,
ProjectRole projectRole) {
final RepositoryRole projectRepositoryRole;
if (projectRole == ProjectRole.MEMBER) {
projectRepositoryRole = roles.projectRoles().member();
} else {
projectRepositoryRole = roles.projectRoles().guest();
}
if (repositoryRole == RepositoryRole.ADMIN || projectRepositoryRole == RepositoryRole.ADMIN) {
return RepositoryRole.ADMIN;
}
if (repositoryRole == RepositoryRole.WRITE || projectRepositoryRole == RepositoryRole.WRITE) {
return RepositoryRole.ADMIN;
}
if (repositoryRole == RepositoryRole.READ || projectRepositoryRole == RepositoryRole.READ) {
return RepositoryRole.READ;
}
return null;
}
Motivation:
To address the migration of mirroring configurations from projects to repositories and to simplify access management,
we decided to:
RepositoryRole
(READ
,WRITE
,ADMIN
). TheADMIN
role has access to the configurations.This change resolves the issue where users with
WRITE
permission for the meta repository were only allowed to creating mirroring configurations.With
RepositoryRole
, access becomes more structured and extensible for future enhancements (e.g., introducing custom roles). While permission-based authentication is replaced here, this does not preclude the future coexistence of role-based and permission-based systems.Modifications:
RepositoryRole
with hierarchical roles (READ
,WRITE
,ADMIN
).RequiresPermission
annotations withRequiresRepositoryRole
.RequiresRole
toRequiresProjectRole
.ProjectRole
, utilizing the hierarchical model to avoid redundant role specifications.Result:
To-do:
Migration plan: