Skip to content

Commit

Permalink
[RBAC] add ORGANIZATION_MEMBER role (#9272)
Browse files Browse the repository at this point in the history
  • Loading branch information
keyihuang committed Oct 16, 2023
1 parent f75d98f commit 5085f5a
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 6 deletions.
1 change: 1 addition & 0 deletions airbyte-api/src/main/openapi/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6296,6 +6296,7 @@ components:
- organization_admin # Permission to update organization settings and manage user roles in an organization and all permissions below
- organization_editor # Permission to create, read, delete, and edit all workspaces within an organization
- organization_reader # Permission to read all workspaces within an organization
- organization_member # Permission to read organization basic information, not having permissions to any workspaces within an organization
- workspace_owner # TODO: remove this old enum. It is equivalent to `workspace_admin`.
- workspace_admin # Permission to create, read, delete, and edit a specific workspace, and also can grant other users permissions to this workspace
- workspace_editor # Permission to create and edit connections within the workspace, but cannot update workspace name or delete the workspace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class PermissionHelper {
PermissionType.ORGANIZATION_ADMIN,
PermissionType.ORGANIZATION_EDITOR,
PermissionType.ORGANIZATION_READER,
PermissionType.ORGANIZATION_MEMBER,
PermissionType.WORKSPACE_OWNER,
PermissionType.WORKSPACE_ADMIN,
PermissionType.WORKSPACE_EDITOR,
Expand All @@ -34,16 +35,24 @@ public class PermissionHelper {
PermissionType.ORGANIZATION_EDITOR, Set.of(
PermissionType.ORGANIZATION_EDITOR,
PermissionType.ORGANIZATION_READER,
PermissionType.ORGANIZATION_MEMBER,
PermissionType.WORKSPACE_EDITOR,
PermissionType.WORKSPACE_READER),

// Organization reader grants access to just the organization reader permission, and also
// Organization reader grants access to all organization-reader-and-lower permissions, and also
// workspace-reader permissions
// for workspaces within the organization.
PermissionType.ORGANIZATION_READER, Set.of(
PermissionType.ORGANIZATION_READER,
PermissionType.ORGANIZATION_MEMBER,
PermissionType.WORKSPACE_READER),

// Organization member grants access to organization member permissions only,
// but does not have permissions to access any workspaces and cannot grant access to workspace level
// permission.
PermissionType.ORGANIZATION_MEMBER, Set.of(
PermissionType.ORGANIZATION_MEMBER),

// Workspace owner (deprecated) is equivalent to workspace admin, and grants access to all
// workspace-admin-and-lower permissions.
PermissionType.WORKSPACE_OWNER, Set.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ void workspaceLevelPermissions(final PermissionType userPermissionType) throws I
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

if (userPermissionType == PermissionType.WORKSPACE_ADMIN) {
Expand All @@ -264,6 +266,8 @@ void workspaceLevelPermissions(final PermissionType userPermissionType) throws I
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

if (userPermissionType == PermissionType.WORKSPACE_EDITOR) {
Expand All @@ -280,6 +284,8 @@ void workspaceLevelPermissions(final PermissionType userPermissionType) throws I
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

if (userPermissionType == PermissionType.WORKSPACE_READER) {
Expand All @@ -295,12 +301,14 @@ void workspaceLevelPermissions(final PermissionType userPermissionType) throws I
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}
}

@ParameterizedTest
@EnumSource(value = PermissionType.class,
names = {"ORGANIZATION_ADMIN", "ORGANIZATION_EDITOR", "ORGANIZATION_READER"})
names = {"ORGANIZATION_ADMIN", "ORGANIZATION_EDITOR", "ORGANIZATION_READER", "ORGANIZATION_MEMBER"})
void organizationLevelPermissions(final PermissionType userPermissionType) throws IOException, JsonValidationException, ConfigNotFoundException {
when(permissionPersistence.listPermissionsByUser(USER_ID)).thenReturn(List.of(new Permission()
.withPermissionType(userPermissionType)
Expand All @@ -327,6 +335,8 @@ void organizationLevelPermissions(final PermissionType userPermissionType) throw
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

if (userPermissionType == PermissionType.ORGANIZATION_EDITOR) {
Expand All @@ -343,6 +353,8 @@ void organizationLevelPermissions(final PermissionType userPermissionType) throw
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

if (userPermissionType == PermissionType.ORGANIZATION_READER) {
Expand All @@ -358,6 +370,25 @@ void organizationLevelPermissions(final PermissionType userPermissionType) throw
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

if (userPermissionType == PermissionType.ORGANIZATION_MEMBER) {
assertEquals(StatusEnum.FAILED, permissionHandler.checkPermissions(getWorkspacePermissionCheck(PermissionType.WORKSPACE_OWNER)).getStatus());
assertEquals(StatusEnum.FAILED, permissionHandler.checkPermissions(getWorkspacePermissionCheck(PermissionType.WORKSPACE_ADMIN)).getStatus());
assertEquals(StatusEnum.FAILED, permissionHandler.checkPermissions(getWorkspacePermissionCheck(PermissionType.WORKSPACE_EDITOR)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getWorkspacePermissionCheck(PermissionType.WORKSPACE_READER)).getStatus());

assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_ADMIN)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.FAILED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}
}

Expand All @@ -384,6 +415,8 @@ void instanceAdminPermissions() throws IOException {
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_EDITOR)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_READER)).getStatus());
assertEquals(StatusEnum.SUCCEEDED,
permissionHandler.checkPermissions(getOrganizationPermissionCheck(PermissionType.ORGANIZATION_MEMBER)).getStatus());
}

@Test
Expand All @@ -396,7 +429,8 @@ void ensureAllPermissionTypesAreCovered() {
PermissionType.WORKSPACE_READER,
PermissionType.ORGANIZATION_ADMIN,
PermissionType.ORGANIZATION_EDITOR,
PermissionType.ORGANIZATION_READER);
PermissionType.ORGANIZATION_READER,
PermissionType.ORGANIZATION_MEMBER);

// If this assertion fails, it means a new PermissionType was added! Please update either the
// `organizationLevelPermissions` or `workspaceLeveLPermissions` tests above this one to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public final class AuthRoleConstants {
public static final String ORGANIZATION_ADMIN = "ORGANIZATION_ADMIN";
public static final String ORGANIZATION_EDITOR = "ORGANIZATION_EDITOR";
public static final String ORGANIZATION_READER = "ORGANIZATION_READER";
public static final String ORGANIZATION_MEMBER = "ORGANIZATION_MEMBER";

private AuthRoleConstants() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public enum OrganizationAuthRole {
ORGANIZATION_ADMIN(400, AuthRoleConstants.ORGANIZATION_ADMIN),
ORGANIZATION_EDITOR(300, AuthRoleConstants.ORGANIZATION_EDITOR),
ORGANIZATION_READER(200, AuthRoleConstants.ORGANIZATION_READER),
ORGANIZATION_MEMBER(100, AuthRoleConstants.ORGANIZATION_MEMBER),
NONE(0, AuthRoleConstants.NONE);

private final int authority;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ properties:
- organization_admin # Permission to update organization settings and manage user roles in an organization and all permissions below
- organization_editor # Permission to create, read, delete, and edit all workspaces within an organization
- organization_reader # Permission to read all workspaces within an organization
- organization_member # Permission to read organization basic information, not having permissions to any workspaces within an organization
- workspace_owner # TODO: remove this old enum. It is equivalent to `workspace_admin`.
- workspace_admin # Permission to create, read, delete, and edit a specific workspace, and also can grant other users permissions to this workspace
- workspace_editor # Permission to create and edit connections within the workspace, but cannot update workspace name or delete the workspace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public class KeycloakTokenValidator implements TokenValidator {
PermissionType.WORKSPACE_EDITOR, AuthRole.EDITOR, PermissionType.WORKSPACE_READER, AuthRole.READER);
private static final Map<PermissionType, OrganizationAuthRole> ORGANIZATION_PERMISSION_TYPE_TO_AUTH_ROLE =
Map.of(PermissionType.ORGANIZATION_ADMIN, OrganizationAuthRole.ORGANIZATION_ADMIN, PermissionType.ORGANIZATION_EDITOR,
OrganizationAuthRole.ORGANIZATION_EDITOR, PermissionType.ORGANIZATION_READER, OrganizationAuthRole.ORGANIZATION_READER);
OrganizationAuthRole.ORGANIZATION_EDITOR, PermissionType.ORGANIZATION_READER, OrganizationAuthRole.ORGANIZATION_READER,
PermissionType.ORGANIZATION_MEMBER, OrganizationAuthRole.ORGANIZATION_MEMBER);

public KeycloakTokenValidator(final HttpClient httpClient,
final AirbyteKeycloakConfiguration keycloakConfiguration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ void testValidateToken() throws Exception {

// TODO: enable this once we are ready to enable getRoles function in KeycloakTokenValidator.
// Set<String> expectedResult = Set.of("ORGANIZATION_READER", "ADMIN", "EDITOR", "READER");
Set<String> expectedResult = Set.of("ORGANIZATION_ADMIN", "ORGANIZATION_EDITOR", "ORGANIZATION_READER", "ADMIN", "EDITOR", "READER");
Set<String> expectedResult =
Set.of("ORGANIZATION_ADMIN", "ORGANIZATION_EDITOR", "ORGANIZATION_READER", "ORGANIZATION_MEMBER", "ADMIN", "EDITOR", "READER");

StepVerifier.create(responsePublisher)
.expectNextMatches(r -> matchSuccessfulResponse(r, expectedUserId, expectedResult))
Expand Down
2 changes: 1 addition & 1 deletion airbyte-webapp/src/core/utils/rbac/rbacPermissionsQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useGetWorkspace } from "core/api";
import { PermissionRead } from "core/request/AirbyteClient";

export const RbacResourceHierarchy = ["INSTANCE", "ORGANIZATION", "WORKSPACE"] as const;
export const RbacRoleHierarchy = ["ADMIN", "EDITOR", "READER"] as const;
export const RbacRoleHierarchy = ["ADMIN", "EDITOR", "READER", "MEMBER"] as const;
export type RbacResource = (typeof RbacResourceHierarchy)[number];
export type RbacRole = (typeof RbacRoleHierarchy)[number];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const permissionStringDictionary: Record<PermissionType, string> = {
organization_admin: "role.admin",
organization_editor: "role.editor",
organization_reader: "role.reader",
organization_member: "role.member",
workspace_admin: "role.admin",
workspace_owner: "role.admin",
workspace_editor: "role.editor",
Expand All @@ -24,6 +25,7 @@ export const permissionDescriptionDictionary: Record<PermissionType, PermissionD
organization_admin: { id: "role.admin.description", values: { resourceType: "organization" } },
organization_editor: { id: "role.editor.description", values: { resourceType: "organization" } },
organization_reader: { id: "role.reader.description", values: { resourceType: "organization" } },
organization_member: { id: "role.member.description", values: { resourceType: "organization" } },
workspace_admin: { id: "role.admin.description", values: { resourceType: "workspace" } },
workspace_owner: { id: "role.admin.description", values: { resourceType: "workspace" } }, // is not and should not be referenced in code. required by types but will be deprecated soon.
workspace_editor: { id: "role.editor.description", values: { resourceType: "workspace" } },
Expand Down

0 comments on commit 5085f5a

Please sign in to comment.