Skip to content

Commit

Permalink
Merge pull request #983 from Altinn/feat/2089-add-role-endpoint
Browse files Browse the repository at this point in the history
Endpoint for fetching current party roles
  • Loading branch information
adamhaeger authored Dec 20, 2024
2 parents ce63710 + 88ae62b commit 699b82a
Show file tree
Hide file tree
Showing 8 changed files with 481 additions and 46 deletions.
127 changes: 81 additions & 46 deletions src/Altinn.App.Api/Controllers/AuthorizationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Altinn.App.Core.Internal.Profile;
using Altinn.App.Core.Internal.Registers;
using Altinn.App.Core.Models;
using Altinn.Platform.Register.Models;
using Authorization.Platform.Authorization.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -44,57 +46,14 @@ IOptions<GeneralSettings> settings
[HttpGet("{org}/{app}/api/authorization/parties/current")]
public async Task<ActionResult> GetCurrentParty(bool returnPartyObject = false)
{
UserContext userContext = await _userHelper.GetUserContext(HttpContext);
int userId = userContext.UserId;

// If selected party is different than party for user self need to verify
if (userContext.UserParty == null || userContext.PartyId != userContext.UserParty.PartyId)
{
bool? isValid = await _authorization.ValidateSelectedParty(userId, userContext.PartyId);

if (isValid == true)
{
if (returnPartyObject)
{
return Ok(userContext.Party);
}

return Ok(userContext.PartyId);
}
else if (userContext.UserParty != null)
{
userContext.Party = userContext.UserParty;
userContext.PartyId = userContext.UserParty.PartyId;
}
else
{
userContext.Party = null;
userContext.PartyId = 0;
}
}

string? cookieValue = Request.Cookies[_settings.GetAltinnPartyCookieName];
if (!int.TryParse(cookieValue, out int partyIdFromCookie))
{
partyIdFromCookie = 0;
}

// Setting cookie to partyID of logged in user if it varies from previus value.
if (partyIdFromCookie != userContext.PartyId)
{
Response.Cookies.Append(
_settings.GetAltinnPartyCookieName,
userContext.PartyId.ToString(CultureInfo.InvariantCulture),
new CookieOptions { Domain = _settings.HostName }
);
}
(Party? currentParty, _) = await GetCurrentPartyAsync(HttpContext);

if (returnPartyObject)
{
return Ok(userContext.Party);
return Ok(currentParty);
}

return Ok(userContext.PartyId);
return Ok(currentParty?.PartyId ?? 0);
}

/// <summary>
Expand Down Expand Up @@ -123,4 +82,80 @@ public async Task<IActionResult> ValidateSelectedParty(int userId, int partyId)
return StatusCode(500, $"Something went wrong when trying to validate party {partyId} for user {userId}");
}
}

/// <summary>
/// Fetches roles for current party.
/// </summary>
/// <returns>List of roles for the current user and party.</returns>
// [Authorize]
// [HttpGet("{org}/{app}/api/authorization/roles")]
// [ProducesResponseType(typeof(IEnumerable<Role), StatusCodes.Status200OK)]
// [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[Authorize]
[HttpGet("{org}/{app}/api/authorization/roles")]
[ProducesResponseType(typeof(IEnumerable<Role>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetRolesForCurrentParty()
{
(Party? currentParty, UserContext userContext) = await GetCurrentPartyAsync(HttpContext);

if (currentParty == null)
{
return BadRequest("Both userId and partyId must be provided.");
}

int userId = userContext.UserId;
IEnumerable<Role> roles = await _authorization.GetUserRoles(userId, currentParty.PartyId);

return Ok(roles);
}

/// <summary>
/// Helper method to retrieve the current party and user context from the HTTP context.
/// </summary>
/// <param name="context">The current HttpContext.</param>
/// <returns>A tuple containing the current party and user context.</returns>
private async Task<(Party? party, UserContext userContext)> GetCurrentPartyAsync(HttpContext context)
{
UserContext userContext = await _userHelper.GetUserContext(context);
int userId = userContext.UserId;

// If selected party is different than party for user self need to verify
if (userContext.UserParty == null || userContext.PartyId != userContext.UserParty.PartyId)
{
bool? isValid = await _authorization.ValidateSelectedParty(userId, userContext.PartyId);
if (isValid != true)
{
// Not valid, fall back to userParty if available
if (userContext.UserParty != null)
{
userContext.Party = userContext.UserParty;
userContext.PartyId = userContext.UserParty.PartyId;
}
else
{
userContext.Party = null;
userContext.PartyId = 0;
}
}
}

// Sync cookie if needed
string? cookieValue = Request.Cookies[_settings.GetAltinnPartyCookieName];
if (!int.TryParse(cookieValue, out int partyIdFromCookie))
{
partyIdFromCookie = 0;
}

if (partyIdFromCookie != userContext.PartyId)
{
Response.Cookies.Append(
_settings.GetAltinnPartyCookieName,
userContext.PartyId.ToString(CultureInfo.InvariantCulture),
new CookieOptions { Domain = _settings.HostName }
);
}

return (userContext.Party, userContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ partial class Telemetry
return activity;
}

internal Activity? StartClientGetPartyRoleListActivity(int userId, int partyId)
{
var activity = ActivitySource.StartActivity($"{Prefix}.GetUserRoles");
activity?.SetUserPartyId(partyId);
activity?.SetUserId(userId);

return activity;
}

internal Activity? StartClientValidateSelectedPartyActivity(int userId, int partyId)
{
var activity = ActivitySource.StartActivity($"{Prefix}.ValidateSelectedParty");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Altinn.Platform.Register.Models;
using Altinn.Platform.Storage.Interface.Models;
using AltinnCore.Authentication.Utils;
using Authorization.Platform.Authorization.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -179,4 +180,49 @@ List<string> actions
}
return MultiDecisionHelper.ValidatePdpMultiDecision(actionsResult, response.Response, user);
}

/// <summary>
/// Retrieves roles for a user on a specified party.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userPartyId">The user party id.</param>
/// <returns>A list of roles for the user on the specified party.</returns>
public async Task<IEnumerable<Role>> GetUserRoles(int userId, int userPartyId)
{
using var activity = _telemetry?.StartClientGetPartyRoleListActivity(userId, userPartyId);

List<Role> roles = new();
string apiUrl = $"roles?coveredByUserId={userId}&offeredByPartyId={userPartyId}";
string token = JwtTokenUtil.GetTokenFromContext(_httpContextAccessor.HttpContext, _settings.RuntimeCookieName);

try
{
HttpResponseMessage response = await _client.GetAsync(token, apiUrl);
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
var deserialized = JsonConvert.DeserializeObject<List<Role>>(responseContent);
if (deserialized is not null)
{
roles = deserialized;
}
}
else
{
throw new Exception("Unexpected response from auth API:" + response.StatusCode);
}
}
catch (Exception ex)
{
_logger.LogError(
ex,
"An error occurred while retrieving roles for userId {UserId} and partyId {PartyId}",
userId,
userPartyId
);
throw;
}

return roles;
}
}
9 changes: 9 additions & 0 deletions src/Altinn.App.Core/Internal/Auth/IAuthorizationClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Altinn.App.Core.Models;
using Altinn.Platform.Register.Models;
using Altinn.Platform.Storage.Interface.Models;
using Authorization.Platform.Authorization.Models;

namespace Altinn.App.Core.Internal.Auth;

Expand Down Expand Up @@ -50,4 +51,12 @@ Task<bool> AuthorizeAction(
/// <param name="actions"></param>
/// <returns></returns>
Task<Dictionary<string, bool>> AuthorizeActions(Instance instance, ClaimsPrincipal user, List<string> actions);

/// <summary>
/// Retrieves roles for a user on a specified party.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="userPartyId">The user party id.</param>
/// <returns>A list of roles for the user on the specified party.</returns>
Task<IEnumerable<Role>> GetUserRoles(int userId, int userPartyId);
}
14 changes: 14 additions & 0 deletions test/Altinn.App.Api.Tests/Mocks/AuthorizationMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Altinn.App.Core.Models;
using Altinn.Platform.Register.Models;
using Altinn.Platform.Storage.Interface.Models;
using Authorization.Platform.Authorization.Models;
using Microsoft.AspNetCore.Http.HttpResults;

namespace Altinn.App.Api.Tests.Mocks;

Expand Down Expand Up @@ -69,4 +71,16 @@ List<string> actions

return authorizedActions;
}

public async Task<IEnumerable<Role>> GetUserRoles(int userId, int userPartyId)
{
await Task.CompletedTask;
List<Role> roles = new List<Role>
{
new Role { Type = "altinn", Value = "bobet" },
new Role { Type = "altinn", Value = "bobes" },
};

return roles;
}
}
Loading

0 comments on commit 699b82a

Please sign in to comment.