Skip to content

Commit

Permalink
Merge pull request #355 from sancsoft/344-project-activities-and-rost…
Browse files Browse the repository at this point in the history
…er-allow-pms-to-edit

344 project activities and roster allow pms to edit
  • Loading branch information
rmaffitsancsoft authored Sep 6, 2024
2 parents 6253c71 + e4157e4 commit 90bf03e
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface DeleteProjectActivityV1Request {
projectId: string;
id: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
<tr>
<td>{{ activity.name }}</td>
<td class="text-right divide-x divide-teal-200">
@if ([HQRole.Executive, HQRole.Administrator] | inRole | async) {
@if (
([HQRole.Executive, HQRole.Administrator] | inRole | async) ||
(projectDetailsService.canManageProjectStatusReport$ | async)
) {
<button
type="button"
class="text-teal-200 hover:text-teal-300 pr-3 uppercase"
Expand All @@ -34,7 +37,10 @@
}
</tbody>
</table>
@if ([HQRole.Executive, HQRole.Administrator] | inRole | async) {
@if (
([HQRole.Executive, HQRole.Administrator] | inRole | async) ||
(projectDetailsService.canManageProjectStatusReport$ | async)
) {
<form [formGroup]="form" (ngSubmit)="onSubmit()" class="p-5">
<div class="border-b border-black pb-1 px-1 font-bold">Add Activity</div>
<div class="mb-3 w-56">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ export class ProjectActivityListComponent {
}

async deleteActivity(activityId: string) {
const projectId = await firstValueFrom(
this.projectDetailsService.projectId$,
);
const confirmation = await firstValueFrom(
this.modalService.confirm(
'Confirmation',
Expand All @@ -96,7 +99,10 @@ export class ProjectActivityListComponent {
if (confirmation) {
try {
await firstValueFrom(
this.hqService.deleteProjectActivityV1({ id: activityId }),
this.hqService.deleteProjectActivityV1({
projectId,
id: activityId,
}),
);
this.projectDetailsService.refresh();
this.toastService.show('Success', 'Activity successfully deleted');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import {
Subject,
switchMap,
tap,
combineLatest,
} from 'rxjs';
import { GetProjectRecordV1 } from '../../models/projects/get-project-v1';
import { HQService } from '../../services/hq.service';
import { GetClientRecordV1 } from '../../models/clients/get-client-v1';
import { GetProjectActivityRecordV1 } from '../../models/projects/get-project-activity-v1';
import { OidcSecurityService } from 'angular-auth-oidc-client';
import { HQRole } from '../../enums/hqrole';

@Injectable({
providedIn: 'root',
Expand All @@ -25,12 +28,16 @@ export class ProjectDetailsService {
client$: Observable<GetClientRecordV1>;
project$: Observable<GetProjectRecordV1>;
activities$: Observable<GetProjectActivityRecordV1[]>;

HQRole = HQRole;
private projectIdSubject = new BehaviorSubject<string | null>(null);
private psrIdSubject = new BehaviorSubject<string | null | undefined>(null);
private refreshSubject = new Subject<void>();
canManageProjectStatusReport$: Observable<boolean>;

constructor(private hqService: HQService) {
constructor(
private hqService: HQService,
private oidcSecurityService: OidcSecurityService,
) {
const projectId$ = this.projectIdSubject.asObservable().pipe(
filter((projectId) => projectId != null),
map((projectId) => projectId!),
Expand Down Expand Up @@ -65,6 +72,23 @@ export class ProjectDetailsService {
map((t) => t.records[0]),
shareReplay({ bufferSize: 1, refCount: false }),
);
this.canManageProjectStatusReport$ = combineLatest({
userData: oidcSecurityService.userData$.pipe(map((t) => t.userData)),
project: this.project$,
}).pipe(
map(
(t) =>
t.userData.roles &&
Array.isArray(t.userData.roles) &&
(t.userData.roles.includes(HQRole.Administrator) ||
t.userData.roles.includes(HQRole.Executive) ||
t.userData.roles.includes(HQRole.Partner) ||
(t.userData.roles.includes(HQRole.Manager) &&
t.project.projectManagerId == t.userData.staff_id)),
),
map((t) => !!t),
shareReplay({ bufferSize: 1, refCount: false }),
);
}

setProjectId(projectId?: string | null) {
Expand All @@ -76,7 +100,6 @@ export class ProjectDetailsService {
setPsrId(psrId?: string | null) {
this.psrIdSubject.next(psrId);
}

refresh() {
this.refreshSubject.next();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
<tr>
<td>{{ staff.name }}</td>
<td class="text-right divide-x divide-teal-200">
@if ([HQRole.Executive, HQRole.Administrator] | inRole | async) {
@if (
([HQRole.Executive, HQRole.Administrator] | inRole | async) ||
(projectDetailsService.canManageProjectStatusReport$ | async)
) {
<button
type="button"
class="text-teal-200 hover:text-teal-300 pl-3 uppercase"
Expand All @@ -24,7 +27,10 @@
}
</tbody>
</table>
@if ([HQRole.Executive, HQRole.Administrator] | inRole | async) {
@if (
([HQRole.Executive, HQRole.Administrator] | inRole | async) ||
(projectDetailsService.canManageProjectStatusReport$ | async)
) {
<form [formGroup]="form" (ngSubmit)="onSubmit()" class="p-5">
<div class="border-b border-black pb-1 px-1 font-bold">
Add Staff Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class DeleteProjectActivityV1
{
public class Request
{
public Guid ProjectId { get; set; }
public Guid Id { get; set; }
}

Expand Down
90 changes: 90 additions & 0 deletions src/dotnet/HQ.Server/Authorization/ProjectsAuthorizationHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using HQ.Server.Data;
using HQ.Server.Data.Models;

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.EntityFrameworkCore;

namespace HQ.Server.Authorization;

public class ProjectsAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Project>
{
private readonly HQDbContext _context;

public ProjectsAuthorizationHandler(HQDbContext context)
{
_context = context;
}

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement, Project resource)
{
var staffId = context.User.GetStaffId();
var isStaff = context.User.IsInRole("staff");
var isExecutive = context.User.IsInRole("executive");
var isAdmin = context.User.IsInRole("administrator");

if (isExecutive || isAdmin)
{
context.Succeed(requirement);
return;
}

if (!isStaff)
{
return;
}

if (!staffId.HasValue)
{
return;
}

if (staffId.Value != resource.ProjectManagerId)
{
return;
}

var staff = await _context.Staff.FindAsync(staffId.Value);
if (staff == null)
{
return;
}
if (requirement.Name == nameof(ProjectsOperation.AddProjectMember))
{
context.Succeed(requirement);
return;
}
switch (requirement.Name)
{
case nameof(ProjectsOperation.AddProjectMember):
if (staffId.Value == resource.ProjectManagerId)
{
context.Succeed(requirement);
}

break;
case nameof(ProjectsOperation.DeleteProjectActivity):
if (staffId.Value == resource.ProjectManagerId)
{
context.Succeed(requirement);
}

break;
case nameof(ProjectsOperation.UpsertProjectActivity):
if (staffId.Value == resource.ProjectManagerId)
{
context.Succeed(requirement);
}

break;
case nameof(ProjectsOperation.RemoveProjectMember):
if (staffId.Value == resource.ProjectManagerId)
{
context.Succeed(requirement);
}

break;
}

}
}
12 changes: 12 additions & 0 deletions src/dotnet/HQ.Server/Authorization/ProjectsOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace HQ.Server.Authorization;

public class ProjectsOperation
{
public static OperationAuthorizationRequirement RemoveProjectMember = new OperationAuthorizationRequirement { Name = nameof(RemoveProjectMember) };
public static OperationAuthorizationRequirement AddProjectMember = new OperationAuthorizationRequirement { Name = nameof(AddProjectMember) };
public static OperationAuthorizationRequirement DeleteProjectActivity = new OperationAuthorizationRequirement { Name = nameof(DeleteProjectActivity) };
public static OperationAuthorizationRequirement UpsertProjectActivity = new OperationAuthorizationRequirement { Name = nameof(UpsertProjectActivity) };

}
Loading

0 comments on commit 90bf03e

Please sign in to comment.