Skip to content

Commit

Permalink
Add SFProjectRights service
Browse files Browse the repository at this point in the history
  • Loading branch information
pmachapman committed Nov 18, 2024
1 parent f3c3da0 commit db21685
Show file tree
Hide file tree
Showing 19 changed files with 773 additions and 182 deletions.
102 changes: 6 additions & 96 deletions src/RealtimeServer/scriptureforge/models/sf-project-rights.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Operation, ProjectRight, ProjectRights } from '../../common/models/project-rights';
import rightsByRole from '../rightsByRole.json';
import { SFProjectRole } from './sf-project-role';

export enum SFProjectDomain {
Expand All @@ -20,106 +21,15 @@ export enum SFProjectDomain {
UserInvites = 'user_invites'
}

// See https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
// Domain is marked optional because each role does not need to list the domains exhaustively
const rightsByRole: Record<SFProjectRole, { [domain in `${SFProjectDomain}`]?: `${Operation}`[] }> = {
sf_observer: {
project_user_configs: ['view_own', 'edit_own'],
texts: ['view'],
sf_note_threads: ['view'],
notes: ['view'],
text_audio: ['view']
},
pt_observer: {
project_user_configs: ['view_own', 'edit_own'],
project: ['view'],
texts: ['view'],
questions: ['view'],
answers: ['view'],
answer_status: ['view'],
answer_comments: ['view'],
likes: ['view'],
biblical_terms: ['view'],
pt_note_threads: ['view'],
sf_note_threads: ['view'],
notes: ['view'],
text_audio: ['view']
},
sf_commenter: {
project_user_configs: ['view_own', 'edit_own'],
texts: ['view'],
sf_note_threads: ['view', 'create', 'delete_own'],
notes: ['view', 'create', 'edit_own', 'delete_own'],
text_audio: ['view']
},
sf_community_checker: {
project_user_configs: ['view_own', 'edit_own'],
texts: ['view'],
questions: ['view'],
answers: ['view', 'create', 'edit_own', 'delete_own'],
answer_status: ['view'],
answer_comments: ['view', 'create', 'edit_own', 'delete_own'],
likes: ['view', 'create', 'delete_own'],
text_audio: ['view']
},
pt_consultant: {
project_user_configs: ['view_own', 'edit_own'],
project: ['view'],
texts: ['view'],
questions: ['view'],
answers: ['view'],
answer_status: ['view'],
answer_comments: ['view'],
likes: ['view'],
biblical_terms: ['view'],
pt_note_threads: ['view', 'create', 'edit', 'delete_own'],
sf_note_threads: ['view', 'create', 'edit', 'delete_own'],
notes: ['view', 'create', 'edit_own', 'delete_own'],
text_audio: ['view']
},
pt_translator: {
project_user_configs: ['view_own', 'edit_own'],
project: ['view'],
texts: ['view', 'edit'],
questions: ['view'],
answers: ['view', 'create', 'edit_own', 'delete_own'],
answer_comments: ['view', 'create', 'edit_own', 'delete_own'],
answer_status: ['view'],
likes: ['view', 'create', 'delete_own'],
biblical_terms: ['view', 'edit'],
pt_note_threads: ['view', 'create', 'edit', 'delete_own'],
sf_note_threads: ['view', 'create', 'edit', 'delete_own'],
notes: ['view', 'create', 'edit_own', 'delete_own'],
text_audio: ['view'],
training_data: ['view', 'create', 'edit_own', 'delete_own'],
drafts: ['view']
},
pt_administrator: {
project_user_configs: ['view_own', 'edit_own'],
project: ['view'],
texts: ['view', 'edit'],
questions: ['view', 'create', 'edit', 'delete'],
answers: ['view', 'create', 'delete', 'edit_own'],
answer_comments: ['view', 'create', 'edit_own', 'delete'],
answer_status: ['view', 'edit'],
likes: ['view', 'create', 'delete_own'],
biblical_terms: ['view', 'edit'],
pt_note_threads: ['view', 'create', 'edit', 'delete'],
sf_note_threads: ['view', 'create', 'edit', 'delete'],
notes: ['view', 'create', 'edit_own', 'delete'],
text_audio: ['view', 'edit', 'create', 'delete'],
training_data: ['view', 'create', 'edit', 'delete'],
drafts: ['view'],
user_invites: ['create']
},
none: {}
};

export class SFProjectRights extends ProjectRights {
constructor() {
super();

for (const [role, rights] of Object.entries(rightsByRole)) {
// See https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
// Domain is marked optional because each role does not need to list the domains exhaustively
for (const [role, rights] of Object.entries(
rightsByRole as Record<SFProjectRole, { [domain in `${SFProjectDomain}`]?: `${Operation}`[] }>
)) {
const rightsForRole: ProjectRight[] = [];
for (const [domain, operations] of Object.entries(rights)) {
for (const operation of operations) rightsForRole.push([domain, operation]);
Expand Down
92 changes: 92 additions & 0 deletions src/RealtimeServer/scriptureforge/rightsByRole.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{
"sf_observer": {
"project_user_configs": ["view_own", "edit_own"],
"texts": ["view"],
"sf_note_threads": ["view"],
"notes": ["view"],
"text_audio": ["view"]
},
"pt_observer": {
"project_user_configs": ["view_own", "edit_own"],
"project": ["view"],
"texts": ["view"],
"questions": ["view"],
"answers": ["view"],
"answer_status": ["view"],
"answer_comments": ["view"],
"likes": ["view"],
"biblical_terms": ["view"],
"pt_note_threads": ["view"],
"sf_note_threads": ["view"],
"notes": ["view"],
"text_audio": ["view"]
},
"sf_commenter": {
"project_user_configs": ["view_own", "edit_own"],
"texts": ["view"],
"sf_note_threads": ["view", "create", "delete_own"],
"notes": ["view", "create", "edit_own", "delete_own"],
"text_audio": ["view"]
},
"sf_community_checker": {
"project_user_configs": ["view_own", "edit_own"],
"texts": ["view"],
"questions": ["view"],
"answers": ["view", "create", "edit_own", "delete_own"],
"answer_status": ["view"],
"answer_comments": ["view", "create", "edit_own", "delete_own"],
"likes": ["view", "create", "delete_own"],
"text_audio": ["view"]
},
"pt_consultant": {
"project_user_configs": ["view_own", "edit_own"],
"project": ["view"],
"texts": ["view"],
"questions": ["view"],
"answers": ["view"],
"answer_status": ["view"],
"answer_comments": ["view"],
"likes": ["view"],
"biblical_terms": ["view"],
"pt_note_threads": ["view", "create", "edit", "delete_own"],
"sf_note_threads": ["view", "create", "edit", "delete_own"],
"notes": ["view", "create", "edit_own", "delete_own"],
"text_audio": ["view"]
},
"pt_translator": {
"project_user_configs": ["view_own", "edit_own"],
"project": ["view"],
"texts": ["view", "edit"],
"questions": ["view"],
"answers": ["view", "create", "edit_own", "delete_own"],
"answer_comments": ["view", "create", "edit_own", "delete_own"],
"answer_status": ["view"],
"likes": ["view", "create", "delete_own"],
"biblical_terms": ["view", "edit"],
"pt_note_threads": ["view", "create", "edit", "delete_own"],
"sf_note_threads": ["view", "create", "edit", "delete_own"],
"notes": ["view", "create", "edit_own", "delete_own"],
"text_audio": ["view"],
"training_data": ["view", "create", "edit_own", "delete_own"],
"drafts": ["view"]
},
"pt_administrator": {
"project_user_configs": ["view_own", "edit_own"],
"project": ["view"],
"texts": ["view", "edit"],
"questions": ["view", "create", "edit", "delete"],
"answers": ["view", "create", "delete", "edit_own"],
"answer_comments": ["view", "create", "edit_own", "delete"],
"answer_status": ["view", "edit"],
"likes": ["view", "create", "delete_own"],
"biblical_terms": ["view", "edit"],
"pt_note_threads": ["view", "create", "edit", "delete"],
"sf_note_threads": ["view", "create", "edit", "delete"],
"notes": ["view", "create", "edit_own", "delete"],
"text_audio": ["view", "edit", "create", "delete"],
"training_data": ["view", "create", "edit", "delete"],
"drafts": ["view"],
"user_invites": ["create"]
},
"none": {}
}
1 change: 1 addition & 0 deletions src/RealtimeServer/scriptureforge/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"outDir": "../lib/cjs/scriptureforge"
},
"references": [{ "path": "../common/tsconfig.build.json" }],
"include": ["rightsByRole.json"],
"exclude": ["**/*.spec.ts"]
}
1 change: 1 addition & 0 deletions src/RealtimeServer/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lib": ["es2017", "dom"],
"composite": true,
"baseUrl": ".",
"resolveJsonModule": true,
"paths": {
"*": ["./typings/*"]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ public async Task<IRpcMethodResult> TransceleratorQuestions(string projectId)
{
try
{
return Ok(await projectService.TransceleratorQuestions(UserId, projectId));
return Ok(await projectService.TransceleratorQuestionsAsync(UserId, projectId));
}
catch (ForbiddenException)
{
Expand Down
12 changes: 12 additions & 0 deletions src/SIL.XForge.Scripture/Models/Operation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SIL.XForge.Scripture.Models;

public static class Operation
{
public const string Create = "create";
public const string Edit = "edit";
public const string Delete = "delete";
public const string View = "view";
public const string EditOwn = "edit_own";
public const string DeleteOwn = "delete_own";
public const string ViewOwn = "view_own";
}
21 changes: 21 additions & 0 deletions src/SIL.XForge.Scripture/Models/SFProjectDomain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace SIL.XForge.Scripture.Models;

public static class SFProjectDomain
{
public const string Texts = "texts";
public const string Project = "project";
public const string ProjectUserConfigs = "project_user_configs";
public const string Questions = "questions";
public const string Answers = "answers";
public const string AnswerComments = "answer_comments";
public const string AnswerStatus = "answer_status";
public const string Likes = "likes";
public const string BiblicalTerms = "biblical_terms";
public const string PTNoteThreads = "pt_note_threads";
public const string SFNoteThreads = "sf_note_threads";
public const string Notes = "notes";
public const string TextAudio = "text_audio";
public const string TrainingData = "training_data";
public const string Drafts = "drafts";
public const string UserInvites = "user_invites";
}
12 changes: 3 additions & 9 deletions src/SIL.XForge.Scripture/Models/SFProjectRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,10 @@ public static class SFProjectRole
public const string CommunityChecker = "sf_community_checker";
public const string Viewer = "sf_observer";

public static bool IsParatextRole(string role)
{
return role switch
public static bool IsParatextRole(string role) =>
role switch
{
SFProjectRole.Administrator
or SFProjectRole.Translator
or SFProjectRole.Consultant
or SFProjectRole.PTObserver
=> true,
Administrator or Translator or Consultant or PTObserver => true,
_ => false,
};
}
}
5 changes: 5 additions & 0 deletions src/SIL.XForge.Scripture/SIL.XForge.Scripture.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<Content Remove="$(SpaRoot)**" />
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
</ItemGroup>
<ItemGroup>
<Content Include="..\RealtimeServer\scriptureforge\rightsByRole.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SIL.XForge\SIL.XForge.csproj" />
</ItemGroup>
Expand Down
9 changes: 9 additions & 0 deletions src/SIL.XForge.Scripture/Services/ISFProjectRights.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using SIL.XForge.Models;

namespace SIL.XForge.Scripture.Services;

public interface ISFProjectRights
{
bool HasRight(Project project, string? userId, string projectDomain, string operation, IOwnedData? data = null);
bool RoleHasRight(Project project, string role, string projectDomain, string operation);
}
2 changes: 1 addition & 1 deletion src/SIL.XForge.Scripture/Services/ISFProjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int daysBeforeExpiration
Task<string> JoinWithShareKeyAsync(string curUserId, string shareKey);
Task<IReadOnlyList<InviteeStatus>> InvitedUsersAsync(string curUserId, string projectId);
bool IsSourceProject(string projectId);
Task<IEnumerable<TransceleratorQuestion>> TransceleratorQuestions(string curUserId, string projectId);
Task<IEnumerable<TransceleratorQuestion>> TransceleratorQuestionsAsync(string curUserId, string projectId);
Task UpdatePermissionsAsync(
string curUserId,
IDocument<SFProject> projectDoc,
Expand Down
Loading

0 comments on commit db21685

Please sign in to comment.