Skip to content
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

Merged
merged 12 commits into from
Dec 24, 2024
Merged
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Fix UI
  • Loading branch information
minwoox committed Nov 14, 2024
commit 1ffab05aae60278a5232dfb5a126e2b25e3cc855
5 changes: 2 additions & 3 deletions webapp/src/dogma/features/repo/RepoPermissionDto.ts
Original file line number Diff line number Diff line change
@@ -17,9 +17,8 @@ export interface RepoCreatorDto {
}

export interface RepoRolePermissionDto {
owner: 'READ' | 'WRITE' | 'REPO_ADMIN';
member: 'READ' | 'WRITE' | 'REPO_ADMIN';
guest: 'READ' | 'WRITE' | 'REPO_ADMIN';
member: 'READ' | 'WRITE' | 'REPO_ADMIN' | null;
guest: 'READ' | 'WRITE' | null;
}

export interface PerUserPermissionDto {
33 changes: 8 additions & 25 deletions webapp/src/dogma/features/repo/RepoPermissionList.tsx
Original file line number Diff line number Diff line change
@@ -5,12 +5,10 @@ import { RepoPermissionDetailDto } from 'dogma/features/repo/RepoPermissionDto';
import { useMemo } from 'react';
import { ChakraLink } from 'dogma/common/components/ChakraLink';
import { RiGitRepositoryPrivateLine } from 'react-icons/ri';
import { AppMemberDetailDto } from 'dogma/features/project/settings/members/AppMemberDto';

export type RepoPermissionListProps<Data extends object> = {
data: Data[];
projectName: string;
members: AppMemberDetailDto[];
};

const RepoPermissionList = <Data extends object>({ data, projectName }: RepoPermissionListProps<Data>) => {
@@ -28,31 +26,16 @@ const RepoPermissionList = <Data extends object>({ data, projectName }: RepoPerm
),
header: 'Name',
}),
columnHelper.accessor((row: RepoPermissionDetailDto) => row.perRolePermissions.owner, {
cell: (info) => (
<Wrap>
{info.getValue().map((permission) => (
<WrapItem key={permission}>
<Tag borderRadius="full" colorScheme="blue" size="sm">
<TagLabel>{permission}</TagLabel>
</Tag>
</WrapItem>
))}
</Wrap>
),
header: 'Owner',
enableSorting: false,
}),
columnHelper.accessor((row: RepoPermissionDetailDto) => row.perRolePermissions.member, {
cell: (info) => (
<Wrap>
{info.getValue().map((permission) => (
<WrapItem key={permission}>
{info.getValue() !== null && (
<WrapItem key={info.getValue()}>
<Tag borderRadius="full" colorScheme="blue" size="sm">
<TagLabel>{permission}</TagLabel>
<TagLabel>{info.getValue() === 'REPO_ADMIN' ? 'ADMIN' : info.getValue()}</TagLabel>
</Tag>
</WrapItem>
))}
)}
</Wrap>
),
header: 'Member',
@@ -61,13 +44,13 @@ const RepoPermissionList = <Data extends object>({ data, projectName }: RepoPerm
columnHelper.accessor((row: RepoPermissionDetailDto) => row.perRolePermissions.guest, {
cell: (info) => (
<Wrap>
{info.getValue().map((permission) => (
<WrapItem key={permission}>
{info.getValue() !== null && (
<WrapItem key={info.getValue()}>
<Tag borderRadius="full" colorScheme="blue" size="sm">
<TagLabel>{permission}</TagLabel>
<TagLabel>{info.getValue()}</TagLabel>
</Tag>
</WrapItem>
))}
)}
</Wrap>
),
header: 'Guest',
Original file line number Diff line number Diff line change
@@ -3,6 +3,6 @@ export interface AddUserPermissionDto {
repoName: string;
data: {
id: string;
permissions: 'READ' | 'WRITE' | 'REPO_ADMIN';
permission: 'READ' | 'WRITE' | 'REPO_ADMIN';
};
}
Original file line number Diff line number Diff line change
@@ -17,8 +17,8 @@ import { useAppDispatch } from 'dogma/hooks';
import { ApiAction } from 'dogma/features/api/apiSlice';
import { AddUserPermissionDto } from 'dogma/features/repo/permissions/AddUserPermissionDto';

const constructPermissions = (permission: string): 'READ' | 'WRITE' | 'REPO_ADMIN' =>
permission === 'write' ? 'WRITE' : permission === 'read' ? 'READ' : null;
const constructPermission = (permission: string): 'READ' | 'WRITE' | 'REPO_ADMIN' =>
permission === 'repo_admin' ? 'REPO_ADMIN' : permission === 'write' ? 'WRITE' : 'READ';

export const ConfirmAddUserPermission = ({
projectName,
@@ -44,7 +44,7 @@ export const ConfirmAddUserPermission = ({
const dispatch = useAppDispatch();
const data = {
id: loginId,
permissions: constructPermissions(permission),
permission: constructPermission(permission),
};

const handleUpdate = async () => {
Original file line number Diff line number Diff line change
@@ -129,8 +129,9 @@ export const NewRepoTokenPermission = ({
>
{tokenOptions.length ? (
<Stack spacing={5} direction="row">
<Radio value="read">Read Only</Radio>
<Radio value="write">Read Write</Radio>
<Radio value="repo_admin">Admin</Radio>
<Radio value="write">Write</Radio>
<Radio value="read">Read</Radio>
</Stack>
) : (
''
Original file line number Diff line number Diff line change
@@ -129,8 +129,9 @@ export const NewRepoUserPermission = ({
>
{memberOptions.length ? (
<Stack spacing={5} direction="row">
<Radio value="read">Read Only</Radio>
<Radio value="write">Read Write</Radio>
<Radio value="repo_admin">Admin</Radio>
<Radio value="write">Write</Radio>
<Radio value="read">Read</Radio>
</Stack>
) : (
''
38 changes: 11 additions & 27 deletions webapp/src/dogma/features/repo/permissions/RolePermissionForm.tsx
Original file line number Diff line number Diff line change
@@ -4,15 +4,9 @@ import { RepoRolePermissionDto } from 'dogma/features/repo/RepoPermissionDto';
import { useState } from 'react';
import { MetadataButton } from 'dogma/common/components/MetadataButton';

const getPermission = (permissions: Array<'READ' | 'WRITE'>) => {
return permissions.find((permission) => permission === 'WRITE')
? 'write'
: permissions.find((permission) => permission === 'READ')
? 'read'
: 'none';
const getPermission = (permission: 'READ' | 'WRITE' | 'REPO_ADMIN' | null) => {
return permission == null ? 'NONE' : permission;
};
const constructPermissions = (permission: string): Array<'READ' | 'WRITE'> =>
permission === 'write' ? ['READ', 'WRITE'] : permission === 'read' ? ['READ'] : [];

export const RolePermissionForm = ({
projectName,
@@ -28,24 +22,15 @@ export const RolePermissionForm = ({
return (
<Box>
<VStack spacing={10} mt={6}>
<FormControl as="fieldset">
<Box borderWidth="1px" borderRadius="lg" overflow="hidden" p={4}>
<FormLabel as="legend">Owner</FormLabel>
<RadioGroup colorScheme="teal" value="write">
<Radio value="write" disabled>
Read Write
</Radio>
</RadioGroup>
</Box>
</FormControl>
<FormControl as="fieldset">
<Box borderWidth="1px" borderRadius="lg" overflow="hidden" p={4}>
<FormLabel as="legend">Member</FormLabel>
<RadioGroup colorScheme="teal" value={member} onChange={setMember}>
<HStack spacing={20}>
<Radio value="read">Read Only</Radio>
<Radio value="write">Read Write</Radio>
<Radio value="none">Forbidden</Radio>
<Radio value="REPO_ADMIN">Admin</Radio>
<Radio value="WRITE">Write</Radio>
<Radio value="READ">Read</Radio>
<Radio value="NONE">Forbidden</Radio>
</HStack>
</RadioGroup>
</Box>
@@ -55,9 +40,9 @@ export const RolePermissionForm = ({
<FormLabel as="legend">Guest</FormLabel>
<RadioGroup colorScheme="teal" value={guest} onChange={setGuest}>
<HStack spacing={20}>
<Radio value="read">Read Only</Radio>
<Radio value="write">Read Write</Radio>
<Radio value="none">Forbidden</Radio>
<Radio value="WRITE">Write</Radio>
<Radio value="READ">Read</Radio>
<Radio value="NONE">Forbidden</Radio>
</HStack>
</RadioGroup>
</Box>
@@ -70,9 +55,8 @@ export const RolePermissionForm = ({
projectName={projectName}
repoName={repoName}
data={{
owner: constructPermissions('write'),
member: constructPermissions(member),
guest: constructPermissions(guest),
member: member === 'NONE' ? null : (member as 'READ' | 'WRITE' | 'REPO_ADMIN'),
guest: guest === 'NONE' ? null : (guest as 'READ' | 'WRITE'),
}}
/>
</Flex>
14 changes: 6 additions & 8 deletions webapp/src/dogma/features/repo/permissions/UserPermission.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import { useMemo } from 'react';
import { ApiAction } from 'dogma/features/api/apiSlice';
import { DeleteMember } from 'dogma/features/project/settings/members/DeleteMember';

type UserAndPermission = [string, string[]];
type UserAndPermission = [string, string];

export const UserPermission = ({
projectName,
@@ -32,13 +32,11 @@ export const UserPermission = ({
columnHelper.accessor((row: UserAndPermission) => row[1], {
cell: (info) => (
<Wrap>
{info.getValue().map((permission) => (
<WrapItem key={permission as string}>
<Tag borderRadius="full" colorScheme="blue" size="sm">
<TagLabel>{permission}</TagLabel>
</Tag>
</WrapItem>
))}
<WrapItem key={info.getValue()}>
<Tag borderRadius="full" colorScheme="blue" size="sm">
<TagLabel>{info.getValue() === 'REPO_ADMIN' ? 'ADMIN' : info.getValue()}</TagLabel>
</Tag>
</WrapItem>
</Wrap>
),
header: 'Permissions',
13 changes: 6 additions & 7 deletions webapp/src/pages/api/v1/projects/[projectName]/index.ts
Original file line number Diff line number Diff line change
@@ -5,24 +5,23 @@ const projectMetadata = {
repos: {
meta: {
name: 'meta',
perRolePermissions: { owner: ['READ', 'WRITE'], member: ['READ'], guest: [] as Array<'READ' | 'WRITE'> },
perRolePermissions: { member: 'READ', guest: null as 'READ' | 'WRITE' | null },
perUserPermissions: {},
perTokenPermissions: {},
creation: { user: 'lb56789@localhost.localdomain', timestamp: '2022-11-23T03:13:50.128853Z' },
},
repo1: {
name: 'repo1',
perRolePermissions: { owner: ['READ', 'WRITE'], member: ['READ', 'WRITE'], guest: ['READ', 'WRITE'] },
perUserPermissions: { 'lz123456@localhost.localdomain': ['READ', 'WRITE'] },
perTokenPermissions: { 'test-token': ['READ'] },
perRolePermissions: { member: 'WRITE', guest: 'WRITE' },
perUserPermissions: { 'lz123456@localhost.localdomain': 'WRITE' },
perTokenPermissions: { 'test-token': 'READ' },
creation: { user: 'lb56789@localhost.localdomain', timestamp: '2022-11-23T03:16:18.853509Z' },
},
repo2: {
name: 'repo2',
perRolePermissions: {
owner: ['READ', 'WRITE'],
member: ['READ', 'WRITE'],
guest: [] as Array<'READ' | 'WRITE'>,
member: 'WRITE',
guest: null as 'READ' | 'WRITE' | null,
},
perUserPermissions: {},
perTokenPermissions: {},
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ const ProjectPermissionPage = () => {
<RepoPermissionList
data={Array.from(Object.values(metadata.repos).filter((repo) => !repo.removal))}
projectName={projectName}
members={Array.from(Object.values(metadata.members))}
/>
)}
</ProjectSettingsView>
17 changes: 8 additions & 9 deletions webapp/tests/dogma/feature/repo/RepoPermissionList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { act, fireEvent, render } from '@testing-library/react';
import RepoPermissionList, { RepoPermissionListProps } from 'dogma/features/repo/RepoPermissionList';
import { RepoPermissionDetailDto } from 'dogma/features/repo/RepoPermissionDto';
import '@testing-library/jest-dom';

describe('RepoPermissionList', () => {
let expectedProps: JSX.IntrinsicAttributes & RepoPermissionListProps<object>;
@@ -11,27 +12,25 @@ describe('RepoPermissionList', () => {
{
name: 'meta',
perRolePermissions: {
owner: ['READ', 'WRITE'],
member: ['READ'],
guest: [] as Array<'READ' | 'WRITE'>,
member: 'READ',
guest: null as 'READ' | 'WRITE' | null,
},
perUserPermissions: {},
perTokenPermissions: {},
creation: { user: 'lb56789@localhost.localdomain', timestamp: '2022-11-23T03:13:50.128853Z' },
},
{
name: 'repo1',
perRolePermissions: { owner: ['READ', 'WRITE'], member: ['READ', 'WRITE'], guest: ['READ', 'WRITE'] },
perUserPermissions: { 'lz123456@localhost.localdomain': ['READ', 'WRITE'] },
perTokenPermissions: { 'test-token': ['READ'] },
perRolePermissions: { member: 'WRITE', guest: 'WRITE' },
perUserPermissions: { 'lz123456@localhost.localdomain': 'WRITE' },
perTokenPermissions: { 'test-token': 'READ' },
creation: { user: 'lb56789@localhost.localdomain', timestamp: '2022-11-23T03:16:18.853509Z' },
},
{
name: 'repo2',
perRolePermissions: {
owner: ['READ', 'WRITE'],
member: ['READ', 'WRITE'],
guest: [] as Array<'READ' | 'WRITE'>,
member: 'WRITE',
guest: null as 'READ' | 'WRITE' | null,
},
perUserPermissions: {},
perTokenPermissions: {},
Loading