Surface project ownership/role on the wire and gate the web UI by it
ProjectResponse now carries OwnerId and a server-resolved MyRole so Editors/Reviewers see read-only fields and no delete/grant-management affordances instead of only finding out via a 403 after the fact. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PuBH9QSv66DPXSSBERmPs6
This commit is contained in:
@@ -132,4 +132,51 @@ public class ProjectAccessTests : ServiceTestFixture
|
||||
var ownerNavigation = Db.Context.Model.FindEntityType(typeof(Project))!.FindNavigation(nameof(Project.Owner))!;
|
||||
Assert.That(ownerNavigation.ForeignKey.DeleteBehavior, Is.EqualTo(DeleteBehavior.Restrict));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task The_creator_of_a_novel_sees_their_role_as_owner()
|
||||
{
|
||||
var writerId = AsNewUser(GlobalRole.Writer);
|
||||
var project = await Projects.CreateAsync(new CreateProjectRequest("Owned by writer"));
|
||||
|
||||
UserContext.UserId = writerId;
|
||||
var role = await Access.GetMyRoleAsync(project);
|
||||
|
||||
Assert.That(role, Is.EqualTo("Owner"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task An_admin_sees_their_role_as_admin_even_on_a_novel_they_do_not_own()
|
||||
{
|
||||
AsNewUser(GlobalRole.Writer);
|
||||
var project = await Projects.CreateAsync(new CreateProjectRequest("Owned by someone else"));
|
||||
|
||||
AsAdmin();
|
||||
var role = await Access.GetMyRoleAsync(project);
|
||||
|
||||
Assert.That(role, Is.EqualTo("Admin"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task A_user_granted_editor_sees_their_role_as_editor()
|
||||
{
|
||||
var project = await Projects.CreateAsync(new CreateProjectRequest("Granted Novel"));
|
||||
var editorId = AsNewUser(GlobalRole.Reviewer);
|
||||
GrantProjectRole(project.Id, editorId, ProjectRole.Editor);
|
||||
|
||||
var role = await Access.GetMyRoleAsync(project);
|
||||
|
||||
Assert.That(role, Is.EqualTo("Editor"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task A_user_with_no_access_sees_a_null_role()
|
||||
{
|
||||
var project = await Projects.CreateAsync(new CreateProjectRequest("Someone Else's Novel"));
|
||||
AsNewUser(GlobalRole.Writer);
|
||||
|
||||
var role = await Access.GetMyRoleAsync(project);
|
||||
|
||||
Assert.That(role, Is.Null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,19 @@ public class ProjectDataTests : ServiceTestFixture
|
||||
Assert.That(afterUnrelatedUpdate.Phase, Is.EqualTo(ProjectPhase.Outlining));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task A_project_response_carries_the_owner_id_and_the_caller_s_role()
|
||||
{
|
||||
var project = await Projects.CreateAsync(new CreateProjectRequest("The Salt Road"));
|
||||
var response = project.ToResponse(await Access.GetMyRoleAsync(project));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(response.OwnerId, Is.EqualTo(UserContext.UserId));
|
||||
Assert.That(response.MyRole, Is.EqualTo("Admin"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Chapters_are_numbered_in_sequence_when_no_number_is_given()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user