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

feat: Access management #877

Merged
merged 49 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f4461f9
Add support for adding owners and admins to portal (wip)
kjetilhau Nov 20, 2024
4475338
Added authorization requirements
kjetilhau Nov 22, 2024
5c10881
Add policies (wip)
kjetilhau Nov 25, 2024
f4a8911
Merge branch 'main' into feat/access-management
kjetilhau Nov 25, 2024
cacd1c5
.
kjetilhau Nov 26, 2024
562f295
Add auth checks to Portals controller
kjetilhau Nov 26, 2024
c095c95
Update api versions
kjetilhau Nov 26, 2024
1c977e3
.
kjetilhau Nov 26, 2024
4437b62
Update auth handlers for all controllers
kjetilhau Nov 26, 2024
0ae75c2
Rename controller actions, concat controller action
kjetilhau Nov 26, 2024
5e4745b
Add account 404 exception
kjetilhau Nov 26, 2024
fc31e81
Remove deprecated endpoints
kjetilhau Nov 26, 2024
4a47b92
.
kjetilhau Nov 27, 2024
0be2ef0
Add owners/admins to models + validation
kjetilhau Nov 27, 2024
4db07ec
Remove unecessary account fields
kjetilhau Nov 27, 2024
790235f
Add db migration
kjetilhau Nov 27, 2024
00b43e4
Update packages
kjetilhau Nov 27, 2024
fad8827
Update models
kjetilhau Nov 27, 2024
a21e23c
Add migration
kjetilhau Nov 27, 2024
218e5ec
Comment out issue
kjetilhau Nov 27, 2024
cca5992
.
kjetilhau Dec 3, 2024
217cbfa
Remove owners
kjetilhau Dec 3, 2024
207b264
Rollback migrations to start over
kjetilhau Dec 3, 2024
8dfab65
Stripped out unused functionality
kjetilhau Dec 3, 2024
6dd8ad4
Remove more unused code
kjetilhau Dec 4, 2024
ecfd3ff
Updated interface references
kjetilhau Dec 4, 2024
a4e6254
Added migration
kjetilhau Dec 4, 2024
e9c7a08
Remove old discarded tests
kjetilhau Dec 4, 2024
856188e
Fixed existing tests by implementing support for fusion profile
kjetilhau Dec 4, 2024
8b0d235
Implemented test for adding portal admins that does not exist
kjetilhau Dec 5, 2024
7dfe530
feat: admin access v1
Noggling Dec 5, 2024
614c29f
Remove Account
kjetilhau Dec 5, 2024
34e9cc8
Add new migration
kjetilhau Dec 5, 2024
b121a25
Merge branch 'feature/access-management' of https://github.com/equino…
kjetilhau Dec 5, 2024
a7cfeaa
Add route to handle duplicate signin for users
kjetilhau Dec 5, 2024
8a258ca
Merge branch 'main' into feature/access-management
kjetilhau Dec 5, 2024
a72c223
.
kjetilhau Dec 5, 2024
67b6f46
Remove unused files
kjetilhau Dec 5, 2024
a05158b
Remove async because its not async
kjetilhau Dec 5, 2024
84e1640
.
kjetilhau Dec 5, 2024
561cbe7
Add slash whatever
kjetilhau Dec 6, 2024
b44397f
.
kjetilhau Dec 6, 2024
91098ac
.
kjetilhau Dec 6, 2024
a8096a1
.
kjetilhau Dec 6, 2024
db67294
fix: disable feature flag in app configuration
Noggling Dec 6, 2024
812a0eb
Add async validator as syncronous due to limitations of dotnet
kjetilhau Dec 9, 2024
e70e692
Merge branch 'main' into feature/access-management
kjetilhau Dec 10, 2024
d524df2
Bump portal version
kjetilhau Dec 10, 2024
342ac87
chore: create pr-877-2203573547.md
kjetilhau Dec 10, 2024
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
10 changes: 10 additions & 0 deletions .changeset/pr-877-2203573547.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

---
"fusion-project-portal": major
---
- Portal Administrators can now be assigned to Portals
- These will be able to manage the respective portal, updating content, apps and contexts


> [!IMPORTANT]
> This change requires database migration.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ namespace Equinor.ProjectExecutionPortal.Application.Commands.OnboardedApps.Onbo

public class OnboardAppCommand : IRequest<Guid>
{
public OnboardAppCommand(string appKey, IList<string> contextTypes)
public OnboardAppCommand(string appKey, List<string> contextTypes)
{
AppKey = appKey;
ContextTypes = contextTypes;
}

public string AppKey { get; }
public IList<string> ContextTypes { get; set; }
public List<string> ContextTypes { get; set; }

public class Handler : IRequestHandler<OnboardAppCommand, Guid>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ namespace Equinor.ProjectExecutionPortal.Application.Commands.OnboardedApps.Upda

public class UpdateOnboardedAppCommand : IRequest<Guid>
{
public UpdateOnboardedAppCommand(string appKey, IList<string> contextTypes)
public UpdateOnboardedAppCommand(string appKey, List<string> contextTypes)
{
AppKey = appKey;
ContextTypes = contextTypes;
}

public string AppKey { get; }
public IList<string> ContextTypes { get; set; }
public List<string> ContextTypes { get; set; }

public class Handler : IRequestHandler<UpdateOnboardedAppCommand, Guid>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Equinor.ProjectExecutionPortal.Application.Helpers;
using Equinor.ProjectExecutionPortal.Application.Services.AccountService;
using Equinor.ProjectExecutionPortal.Application.Services.ContextTypeService;
using Equinor.ProjectExecutionPortal.Domain.Common;
using Equinor.ProjectExecutionPortal.Domain.Entities;
using Equinor.ProjectExecutionPortal.Infrastructure;
using MediatR;
Expand All @@ -8,41 +10,67 @@ namespace Equinor.ProjectExecutionPortal.Application.Commands.Portals.CreatePort

public class CreatePortalCommand : IRequest<Guid>
{
public CreatePortalCommand(string name, string shortName, string subText, string? description, string icon, IList<string> contextTypes)
public CreatePortalCommand(string name,
string shortName,
string subText,
string? description,
string icon,
List<string> contextTypes,
List<AccountIdentifier> admins)
{
Name = name;
ShortName = shortName;
SubText = subText;
Description = description;
Icon = icon;
ContextTypes = contextTypes;
Admins = admins;
}

public string Name { get; set; }
public string ShortName { get; set; }
public string SubText { get; set; }
public string? Description { get; set; }
public string Icon { get; set; }
public IList<string> ContextTypes { get; set; }
public List<string> ContextTypes { get; set; }
public List<AccountIdentifier> Admins { get; }

public class Handler : IRequestHandler<CreatePortalCommand, Guid>
{
private readonly IReadWriteContext _readWriteContext;
private readonly IAccountService _accountService;
private readonly IContextTypeService _contextTypeService;
private readonly IReadWriteContext _readWriteContext;

public Handler(IReadWriteContext readWriteContext, IContextTypeService contextTypeService)
public Handler(IReadWriteContext readWriteContext, IContextTypeService contextTypeService, IAccountService accountService)
{
_readWriteContext = readWriteContext;
_contextTypeService = contextTypeService;
_accountService = accountService;
}

public async Task<Guid> Handle(CreatePortalCommand command, CancellationToken cancellationToken)
{
var slug = SlugHelper.Sluggify(command.Name);

var portal = new Portal(slug, command.Name, command.ShortName, command.SubText, command.Description, command.Icon);
var portal = new Portal(
slug,
command.Name,
command.ShortName,
command.SubText,
command.Description,
command.Icon);

portal.UpdateContextTypes(await _contextTypeService.GetAllowedContextTypesByKeys(command.ContextTypes, cancellationToken));

var fusionProfiles = await _accountService.ResolveProfilesOrThrowAsync(command.Admins.ToList(), cancellationToken);

var admins = command.Admins.Select(admin =>
{
var fusionProfile = fusionProfiles.FirstOrDefault(x => x.Profile!.AzureUniqueId == admin.AzureUniqueId)!.Profile;
return new PortalAdmin { PortalId = portal.Id, AzureUniqueId = fusionProfile!.AzureUniqueId!.Value };
}).ToList();

portal.AddContextTypes(await _contextTypeService.GetAllowedContextTypesByKeys(command.ContextTypes, cancellationToken));
portal.UpdateAdmins(admins);

await _readWriteContext.Set<Portal>().AddAsync(portal, cancellationToken);

Expand All @@ -51,4 +79,4 @@ public async Task<Guid> Handle(CreatePortalCommand command, CancellationToken ca
return portal.Id;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Equinor.ProjectExecutionPortal.Application.Helpers;
using Equinor.ProjectExecutionPortal.Application.Services.AccountService;
using Equinor.ProjectExecutionPortal.Application.Services.ContextTypeService;
using Equinor.ProjectExecutionPortal.Domain.Common;
using Equinor.ProjectExecutionPortal.Domain.Common.Exceptions;
using Equinor.ProjectExecutionPortal.Domain.Entities;
using Equinor.ProjectExecutionPortal.Infrastructure;
Expand All @@ -10,7 +12,15 @@ namespace Equinor.ProjectExecutionPortal.Application.Commands.Portals.UpdatePort

public class UpdatePortalCommand : IRequest<Guid>
{
public UpdatePortalCommand(Guid id, string name, string shortName, string subText, string? description, string icon, IList<string> contextTypes)
public UpdatePortalCommand(
Guid id,
string name,
string shortName,
string subText,
string? description,
string icon,
List<string> contextTypes,
List<AccountIdentifier> admins)
{
Id = id;
Name = name;
Expand All @@ -19,6 +29,7 @@ public UpdatePortalCommand(Guid id, string name, string shortName, string subTex
Description = description;
Icon = icon;
ContextTypes = contextTypes;
Admins = admins;
}

public Guid Id { get; }
Expand All @@ -27,39 +38,54 @@ public UpdatePortalCommand(Guid id, string name, string shortName, string subTex
public string SubText { get; }
public string? Description { get; }
public string Icon { get; }
public IList<string> ContextTypes { get; }
public List<string> ContextTypes { get; }
public List<AccountIdentifier> Admins { get; private set; }

public class Handler : IRequestHandler<UpdatePortalCommand, Guid>
{
private readonly IReadWriteContext _readWriteContext;
private readonly IContextTypeService _contextTypeService;
private readonly IAccountService _accountService;

public Handler(IReadWriteContext readWriteContext, IContextTypeService contextTypeService)
public Handler(IReadWriteContext readWriteContext, IContextTypeService contextTypeService, IAccountService accountService)
{
_readWriteContext = readWriteContext;
_contextTypeService = contextTypeService;
_accountService = accountService;
}

public async Task<Guid> Handle(UpdatePortalCommand command, CancellationToken cancellationToken)
{
var entity = await _readWriteContext.Set<Portal>()
.Include(x => x.ContextTypes)
.FirstOrDefaultAsync(x => x.Id == command.Id, cancellationToken);
var portal = await _readWriteContext.Set<Portal>()
.Include(portal => portal.ContextTypes)
.Include(portal => portal.Admins)
.FirstOrDefaultAsync(portal => portal.Id == command.Id, cancellationToken);

if (entity == null)
if (portal is null)
{
throw new NotFoundException(nameof(Portal), command.Id);
}

var slug = SlugHelper.Sluggify(command.Name);

entity.Update(slug, command.Name, command.ShortName, command.SubText, command.Description, command.Icon);
portal.Update(slug, command.Name, command.ShortName, command.SubText, command.Description, command.Icon);

entity.AddContextTypes(await _contextTypeService.GetAllowedContextTypesByKeys(command.ContextTypes, cancellationToken));
portal.UpdateContextTypes(await _contextTypeService.GetAllowedContextTypesByKeys(command.ContextTypes, cancellationToken));

var fusionProfiles = await _accountService.ResolveProfilesOrThrowAsync(command.Admins.ToList(), cancellationToken);

var admins = command.Admins.Select(admin =>
{
var fusionProfile = fusionProfiles.FirstOrDefault(x => x.Profile!.AzureUniqueId == admin.AzureUniqueId)!.Profile;
return new PortalAdmin { PortalId = portal.Id, AzureUniqueId = fusionProfile!.AzureUniqueId!.Value };
}).ToList();


portal.UpdateAdmins(admins);

await _readWriteContext.SaveChangesAsync(cancellationToken);

return entity.Id;
return portal.Id;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Fusion.Integration.Abstractions" Version="8.0.7" />
<PackageReference Include="Fusion.Integration.Apps.Abstractions" Version="8.0.0" />
<PackageReference Include="Fusion.Integration.Abstractions" Version="8.1.0" />
<PackageReference Include="Fusion.Integration.Apps.Abstractions" Version="8.1.0" />
<PackageReference Include="MediatR" Version="12.4.1" />
</ItemGroup>

Expand All @@ -18,4 +18,8 @@
<ProjectReference Include="..\Equinor.ProjectExecutionPortal.Infrastructure\Equinor.ProjectExecutionPortal.Infrastructure.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Queries\Accounts\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

namespace Equinor.ProjectExecutionPortal.Application.Queries.ContextTypes.GetContextTypes;

public class GetContextTypesQuery : QueryBase<IList<ContextTypeDto>>
public class GetContextTypesQuery : QueryBase<List<ContextTypeDto>>
{
public class Handler : IRequestHandler<GetContextTypesQuery, IList<ContextTypeDto>>
public class Handler : IRequestHandler<GetContextTypesQuery, List<ContextTypeDto>>
{
private readonly IReadWriteContext _context;
private readonly IMapper _mapper;
Expand All @@ -19,8 +19,7 @@ public Handler(IReadWriteContext context, IMapper mapper)
_mapper = mapper;
}

public async Task<IList<ContextTypeDto>> Handle(GetContextTypesQuery request,
CancellationToken cancellationToken)
public async Task<List<ContextTypeDto>> Handle(GetContextTypesQuery request, CancellationToken cancellationToken)
{
var entity = await _context.Set<Domain.Entities.ContextType>()
.AsNoTracking()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace Equinor.ProjectExecutionPortal.Application.Queries.OnboardedApps.GetOnboardedApps;

public class GetOnboardedAppsQuery : QueryBase<IList<OnboardedAppDto>>
public class GetOnboardedAppsQuery : QueryBase<List<OnboardedAppDto>>
{
public class Handler : IRequestHandler<GetOnboardedAppsQuery, IList<OnboardedAppDto>>
public class Handler : IRequestHandler<GetOnboardedAppsQuery, List<OnboardedAppDto>>
{
private readonly IReadWriteContext _context;
private readonly IMapper _mapper;
Expand All @@ -22,7 +22,7 @@ public Handler(IReadWriteContext context, IMapper mapper, IAppService appService
_appService = appService;
}

public async Task<IList<OnboardedAppDto>> Handle(GetOnboardedAppsQuery request, CancellationToken cancellationToken)
public async Task<List<OnboardedAppDto>> Handle(GetOnboardedAppsQuery request, CancellationToken cancellationToken)
{
var entity = await _context.Set<Domain.Entities.OnboardedApp>()
.Include(x => x.ContextTypes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class OnboardedAppDto : AuditDto, IMapFrom<Domain.Entities.OnboardedApp>
public string? DisplayName { get; set; }
public string? Description { get; set; }
public App? AppInformation { get; set; }
public IList<ContextTypeDto> ContextTypes { get; set; } = [];
public List<ContextTypeDto> ContextTypes { get; set; } = [];

public void SupplyWithFusionData(App app)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace Equinor.ProjectExecutionPortal.Application.Queries.OnboardedContexts.GetOnboardedContexts;

public class GetOnboardedContextsQuery : QueryBase<IList<OnboardedContextDto>>
public class GetOnboardedContextsQuery : QueryBase<List<OnboardedContextDto>>
{
public class Handler : IRequestHandler<GetOnboardedContextsQuery, IList<OnboardedContextDto>>
public class Handler : IRequestHandler<GetOnboardedContextsQuery, List<OnboardedContextDto>>
{
private readonly IReadWriteContext _context;
private readonly IMapper _mapper;
Expand All @@ -22,7 +22,7 @@ public Handler(IReadWriteContext context, IMapper mapper, IContextService contex
_contextService = contextService;
}

public async Task<IList<OnboardedContextDto>> Handle(GetOnboardedContextsQuery request, CancellationToken cancellationToken)
public async Task<List<OnboardedContextDto>> Handle(GetOnboardedContextsQuery request, CancellationToken cancellationToken)
{
var entity = await _context.Set<Domain.Entities.OnboardedContext>()
.AsNoTracking()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public Handler(IReadWriteContext readWriteContext, IMapper mapper)
.AsNoTracking()
.Include(x => x.ContextTypes)
.Include(x => x.Configuration)
.Include(x => x.Admins)
.FirstOrDefaultAsync(x => x.Id == request.PortalId, cancellationToken);

var portal = _mapper.Map<Portal?, PortalDto?>(entity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Equinor.ProjectExecutionPortal.Application.Queries.Portals.GetPortalAppKeys;

public class GetContextualAndGlobalAppKeysByPortalAndContextQuery : QueryBase<IList<string>>
public class GetContextualAndGlobalAppKeysByPortalAndContextQuery : QueryBase<List<string>>
{
public GetContextualAndGlobalAppKeysByPortalAndContextQuery(Guid portalId, Guid contextId)
{
Expand All @@ -19,7 +19,7 @@ public GetContextualAndGlobalAppKeysByPortalAndContextQuery(Guid portalId, Guid
public Guid PortalId { get; }
public Guid ContextId { get; }

public class Handler : IRequestHandler<GetContextualAndGlobalAppKeysByPortalAndContextQuery, IList<string>>
public class Handler : IRequestHandler<GetContextualAndGlobalAppKeysByPortalAndContextQuery, List<string>>
{
private readonly IReadWriteContext _readWriteContext;

Expand All @@ -32,7 +32,7 @@ public Handler(IReadWriteContext readWriteContext, IContextService contextServic
_contextService = contextService;
}

public async Task<IList<string>> Handle(GetContextualAndGlobalAppKeysByPortalAndContextQuery request, CancellationToken cancellationToken)
public async Task<List<string>> Handle(GetContextualAndGlobalAppKeysByPortalAndContextQuery request, CancellationToken cancellationToken)
{
var fusionContext = await _contextService.GetFusionContext(request.ContextId, cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Equinor.ProjectExecutionPortal.Application.Queries.Portals.GetPortalApps;

public class GetContextualAndGlobalAppsByPortalAndContextQuery : QueryBase<IList<PortalAppDto>>
public class GetContextualAndGlobalAppsByPortalAndContextQuery : QueryBase<List<PortalAppDto>>
{
public GetContextualAndGlobalAppsByPortalAndContextQuery(Guid portalId, Guid contextId)
{
Expand All @@ -21,7 +21,7 @@ public GetContextualAndGlobalAppsByPortalAndContextQuery(Guid portalId, Guid con
public Guid PortalId { get; }
public Guid ContextId { get; }

public class Handler : IRequestHandler<GetContextualAndGlobalAppsByPortalAndContextQuery, IList<PortalAppDto>>
public class Handler : IRequestHandler<GetContextualAndGlobalAppsByPortalAndContextQuery, List<PortalAppDto>>
{
private readonly IReadWriteContext _readWriteContext;
private readonly IAppService _appService;
Expand All @@ -36,7 +36,7 @@ public Handler(IReadWriteContext readWriteContext, IAppService appService, IMapp
_contextService = contextService;
}

public async Task<IList<PortalAppDto>> Handle(GetContextualAndGlobalAppsByPortalAndContextQuery request, CancellationToken cancellationToken)
public async Task<List<PortalAppDto>> Handle(GetContextualAndGlobalAppsByPortalAndContextQuery request, CancellationToken cancellationToken)
{
var fusionContext = await _contextService.GetFusionContext(request.ContextId, cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,8 @@ public Handler(IReadWriteContext readWriteContext, IAppService appService, IPort

var portalOnboardedAppDto = _mapper.Map<PortalApp, PortalOnboardedAppDto>(portalApps.First());

await _portalService.EnrichPortalAppWithContextIds(portalOnboardedAppDto, portalContextIds, cancellationToken);

await _portalService.SetAppAsActiveInPortal(portalOnboardedAppDto, cancellationToken);
_portalService.EnrichPortalAppWithContextIds(portalOnboardedAppDto, portalContextIds);
_portalService.SetAppAsActiveInPortal(portalOnboardedAppDto);

await _appService.EnrichWithFusionAppData(portalOnboardedAppDto.OnboardedApp, cancellationToken);

Expand Down
Loading
Loading