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: Authorized parties endpoint in enduser API #661

Merged
merged 15 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
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
27 changes: 20 additions & 7 deletions docs/schema/V1/schema.verified.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
schema {
query: DialogQueries
query: Queries
}

type Activity {
Expand Down Expand Up @@ -34,6 +34,18 @@ type ApiActionEndpoint {
sunsetAt: DateTime
}

type AuthorizedParty {
party: String!
name: String!
partyType: String!
isDeleted: Boolean!
hasKeyRole: Boolean!
isMainAdministrator: Boolean!
isAccessManager: Boolean!
hasOnlyAccessToSubParties: Boolean!
subParties: [AuthorizedParty!]
}

type Content {
type: ContentType!
value: [Localization!]!
Expand Down Expand Up @@ -63,11 +75,6 @@ type Dialog {
seenSinceLastUpdate: [SeenLog!]!
}

type DialogQueries @authorize(policy: "enduser") {
dialogById(dialogId: UUID!): Dialog!
searchDialogs(input: SearchDialogInput!): SearchDialogsPayload!
}

type Element {
id: UUID!
type: URL
Expand Down Expand Up @@ -103,6 +110,12 @@ type Localization {
cultureCode: String!
}

type Queries @authorize(policy: "enduser") {
dialogById(dialogId: UUID!): Dialog!
searchDialogs(input: SearchDialogInput!): SearchDialogsPayload!
parties: [AuthorizedParty!]!
}

type SearchDialog {
id: UUID!
org: String!
Expand Down Expand Up @@ -239,4 +252,4 @@ scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time"

scalar URL @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc3986")

scalar UUID @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc4122")
scalar UUID @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc4122")
87 changes: 86 additions & 1 deletion docs/schema/V1/swagger.verified.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
{
"openapi": "3.0.0",
"info": {
"title": "Dialogporten",
Expand Down Expand Up @@ -1606,6 +1606,42 @@
]
}
},
"/api/v1/enduser/parties": {
"get": {
"tags": [
"Enduser"
],
"summary": "Gets the list of authorized parties for the end user",
"description": "Gets the list of authorized parties for the end user. For more information see the documentation (link TBD).",
"operationId": "GetParties",
"responses": {
"200": {
"description": "The list of authorized parties for the end user",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetPartiesDto"
}
}
}
}
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Forbidden"
}
},
"security": [
{
"JWTBearerAuth": []
}
]
}
},
"/api/v1/enduser/dialogs/{dialogId}/seenlog": {
"get": {
"tags": [
Expand Down Expand Up @@ -3732,6 +3768,55 @@
}
}
},
"GetPartiesDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"authorizedParties": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AuthorizedPartyDto"
}
}
}
},
"AuthorizedPartyDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"party": {
"type": "string"
},
"name": {
"type": "string"
},
"partyType": {
"type": "string"
},
"isDeleted": {
"type": "boolean"
},
"hasKeyRole": {
"type": "boolean"
},
"isMainAdministrator": {
"type": "boolean"
},
"isAccessManager": {
"type": "boolean"
},
"hasOnlyAccessToSubParties": {
"type": "boolean"
},
"subParties": {
"type": "array",
"nullable": true,
"items": {
"$ref": "#/components/schemas/AuthorizedPartyDto"
}
}
}
},
"SearchDialogSeenLogDto": {
"type": "object",
"additionalProperties": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services
.AddTransient<IUserOrganizationRegistry, UserOrganizationRegistry>()
.AddTransient<IUserResourceRegistry, UserResourceRegistry>()
.AddTransient<IUserNameRegistry, UserNameRegistry>()
.AddTransient<IUserParties, UserParties>()
.AddTransient<IDialogActivityService, DialogActivityService>()
.AddTransient<IClock, Clock>()
.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Digdir.Domain.Dialogporten.Application.Common.Extensions;
using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;
using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
using Digdir.Domain.Dialogporten.Domain.Parties;

namespace Digdir.Domain.Dialogporten.Application.Common;

public interface IUserParties
{
public Task<AuthorizedPartiesResult> GetUserParties(CancellationToken cancellationToken = default);
}

public class UserParties : IUserParties
{
private readonly IUser _user;
private readonly IAltinnAuthorization _altinnAuthorization;

public UserParties(IUser user, IAltinnAuthorization altinnAuthorization)
{
_user = user ?? throw new ArgumentNullException(nameof(user));
_altinnAuthorization = altinnAuthorization ?? throw new ArgumentNullException(nameof(altinnAuthorization));
}

public Task<AuthorizedPartiesResult> GetUserParties(CancellationToken cancellationToken = default) =>
_user.TryGetPid(out var pid) &&
NorwegianPersonIdentifier.TryParse(NorwegianPersonIdentifier.PrefixWithSeparator + pid,
out var partyIdentifier)
? _altinnAuthorization.GetAuthorizedParties(partyIdentifier, cancellationToken)
: Task.FromResult(new AuthorizedPartiesResult());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Digdir.Domain.Dialogporten.Domain.Parties.Abstractions;

namespace Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;

public class AuthorizedPartiesResult
{
public List<AuthorizedParty> AuthorizedParties { get; init; } = new();
}

public class AuthorizedParty
{
public string Party { get; init; } = null!;
public string Name { get; init; } = null!;
public AuthorizedPartyType PartyType { get; init; }
public bool IsDeleted { get; init; }
public bool HasKeyRole { get; init; }
public bool IsMainAdministrator { get; init; }
public bool IsAccessManager { get; init; }
public bool HasOnlyAccessToSubParties { get; init; }
public List<string> AuthorizedResources { get; init; } = new();
public List<AuthorizedParty>? SubParties { get; init; }
}

public enum AuthorizedPartyType
{
Person,
Organization
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Parties.Abstractions;

namespace Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;

Expand All @@ -13,4 +14,7 @@ public Task<DialogSearchAuthorizationResult> GetAuthorizedResourcesForSearch(
List<string> constraintServiceResources,
string? endUserId = null,
CancellationToken cancellationToken = default);

public Task<AuthorizedPartiesResult> GetAuthorizedParties(IPartyIdentifier authenticatedParty,
CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Parties.Queries.Get;

public class GetPartiesDto
{
public List<AuthorizedPartyDto> AuthorizedParties { get; init; } = new();
}

public class AuthorizedPartyDto
{
public string Party { get; init; } = null!;
public string Name { get; init; } = null!;
public string PartyType { get; init; } = null!;
public bool IsDeleted { get; init; }
public bool HasKeyRole { get; init; }
public bool IsMainAdministrator { get; init; }
public bool IsAccessManager { get; init; }
public bool HasOnlyAccessToSubParties { get; init; }
public List<AuthorizedPartyDto>? SubParties { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Application.Common;
using MediatR;

namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Parties.Queries.Get;

public sealed class GetPartiesQuery : IRequest<GetPartiesDto>;

internal sealed class GetPartiesQueryHandler : IRequestHandler<GetPartiesQuery, GetPartiesDto>
{
private readonly IUserParties _userParties;
private readonly IMapper _mapper;

public GetPartiesQueryHandler(IUserParties userParties, IMapper mapper)
{
_userParties = userParties;
_mapper = mapper;
}

public async Task<GetPartiesDto> Handle(GetPartiesQuery request, CancellationToken cancellationToken)
{
var authorizedPartiesResult = await _userParties.GetUserParties(cancellationToken);
return _mapper.Map<GetPartiesDto>(authorizedPartiesResult);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;

namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Parties.Queries.Get;

internal sealed class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<AuthorizedPartiesResult, GetPartiesDto>();
CreateMap<AuthorizedParty, AuthorizedPartyDto>();
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Get;
using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search;
using Digdir.Domain.Dialogporten.GraphQL.Common.Authorization;
using Digdir.Domain.Dialogporten.GraphQL.EndUser.DialogById;
using Digdir.Domain.Dialogporten.GraphQL.EndUser.SearchDialogs;
using HotChocolate.Authorization;
using MediatR;

namespace Digdir.Domain.Dialogporten.GraphQL.EndUser;

[Authorize(Policy = AuthorizationPolicy.EndUser)]
public class DialogQueries
public partial class Queries
{
public async Task<Dialog> GetDialogById(
[Service] ISender mediator,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Parties.Queries.Get;

namespace Digdir.Domain.Dialogporten.GraphQL.EndUser.Parties;

public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<AuthorizedPartyDto, AuthorizedParty>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Digdir.Domain.Dialogporten.GraphQL.EndUser.Parties;

public class AuthorizedParty
{
public string Party { get; init; } = null!;
public string Name { get; init; } = null!;
public string PartyType { get; init; } = null!;
public bool IsDeleted { get; init; }
public bool HasKeyRole { get; init; }
public bool IsMainAdministrator { get; init; }
public bool IsAccessManager { get; init; }
public bool HasOnlyAccessToSubParties { get; init; }
public List<AuthorizedParty>? SubParties { get; init; }
}
20 changes: 20 additions & 0 deletions src/Digdir.Domain.Dialogporten.GraphQL/EndUser/PartyQueries.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Parties.Queries.Get;
using Digdir.Domain.Dialogporten.GraphQL.EndUser.Parties;
using MediatR;

namespace Digdir.Domain.Dialogporten.GraphQL.EndUser;

public partial class Queries
{
public async Task<List<AuthorizedParty>> GetParties(
[Service] ISender mediator,
[Service] IMapper mapper,
CancellationToken cancellationToken)
{
var request = new GetPartiesQuery();
var result = await mediator.Send(request, cancellationToken);

return result.AuthorizedParties.Select(mapper.Map<AuthorizedParty>).ToList();
elsand marked this conversation as resolved.
Show resolved Hide resolved
}
}
7 changes: 7 additions & 0 deletions src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Queries.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Digdir.Domain.Dialogporten.GraphQL.Common.Authorization;
using HotChocolate.Authorization;

namespace Digdir.Domain.Dialogporten.GraphQL.EndUser;

[Authorize(Policy = AuthorizationPolicy.EndUser)]
public partial class Queries;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Digdir.Domain.Dialogporten.GraphQL.EndUser;
using Digdir.Domain.Dialogporten.GraphQL.EndUser.Parties;
elsand marked this conversation as resolved.
Show resolved Hide resolved
using Digdir.Domain.Dialogporten.Infrastructure.Persistence;

namespace Digdir.Domain.Dialogporten.GraphQL;
Expand All @@ -13,7 +14,7 @@ public static IServiceCollection AddDialogportenGraphQl(
.AddAuthorization()
.RegisterDbContext<DialogDbContext>()
.AddDiagnosticEventListener<ApplicationInsightEventListener>()
.AddQueryType<DialogQueries>()
.AddQueryType<Queries>()
.Services;
}
}
Loading