Skip to content

Commit

Permalink
API endpoint for getting UserProfile by username (#105)
Browse files Browse the repository at this point in the history
* API endpoint for getting UserProfile by username
#104
Adds an API endpoint in altinn-profile:
profile/api/v1/users/?username={username}

which consumes the newly added SBL bridge API:
/sblbridge/profile/api/users/?username={username}

* fixed øæå testdata issue

* - Moved implementation into separate controller for internal use only
- Simplified implementation to a single POST endpoint with lookup model

* Forgot to set controller endpoints hidden from swagger

* Fixed wrong namespace on UserProfileLookup model

---------

Co-authored-by: Jon Kjetil Øye <[email protected]>
  • Loading branch information
jonkjetiloye and Jon Kjetil Øye authored Nov 7, 2023
1 parent 7adce27 commit d2d226b
Show file tree
Hide file tree
Showing 8 changed files with 654 additions and 2 deletions.
71 changes: 71 additions & 0 deletions src/Altinn.Profile/Controllers/UserProfileInternalController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Threading.Tasks;
using Altinn.Platform.Profile.Models;
using Altinn.Profile.Models;
using Altinn.Profile.Services.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Altinn.Profile.Controllers
{
/// <summary>
/// Controller for user profile API endpoints for internal consumption (e.g. Authorization) requiring neither authenticated user token nor access token authorization.
/// </summary>
[Route("profile/api/v1/internal/user")]
[ApiExplorerSettings(IgnoreApi = true)]
[Consumes("application/json")]
[Produces("application/json")]
public class UserProfileInternalController : Controller
{
private readonly IUserProfiles _userProfilesWrapper;

/// <summary>
/// Initializes a new instance of the <see cref="UserProfileInternalController"/> class
/// </summary>
/// <param name="userProfilesWrapper">The users wrapper</param>
public UserProfileInternalController(IUserProfiles userProfilesWrapper)
{
_userProfilesWrapper = userProfilesWrapper;
}

/// <summary>
/// Gets the user profile for a given user identified by one of the available types of user identifiers:
/// UserId (from Altinn 2 Authn UserProfile)
/// Username (from Altinn 2 Authn UserProfile)
/// SSN/Dnr (from Freg)
/// Uuid (from Altinn 2 Party/UserProfile implementation will be added later)
/// </summary>
/// <param name="userProfileLookup">Input model for providing one of the supported lookup parameters</param>
/// <returns>User profile of the given user</returns>
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserProfile>> Get([FromBody] UserProfileLookup userProfileLookup)
{
UserProfile result;
if (userProfileLookup != null && userProfileLookup.UserId != 0)
{
result = await _userProfilesWrapper.GetUser(userProfileLookup.UserId);
}
else if (!string.IsNullOrWhiteSpace(userProfileLookup?.Username))
{
result = await _userProfilesWrapper.GetUserByUsername(userProfileLookup.Username);
}
else if (!string.IsNullOrWhiteSpace(userProfileLookup?.Ssn))
{
result = await _userProfilesWrapper.GetUser(userProfileLookup.Ssn);
}
else
{
return BadRequest();
}

if (result == null)
{
return NotFound();
}

return Ok(result);
}
}
}
27 changes: 27 additions & 0 deletions src/Altinn.Profile/Models/UserProfileLookup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Altinn.Profile.Models
{
/// <summary>
/// Input model for internal UserProfile lookup requests, where one of the lookup identifiers available must be set for performing the lookup request:
/// UserId (from Altinn 2 Authn UserProfile)
/// Username (from Altinn 2 Authn UserProfile)
/// SSN/Dnr (from Freg)
/// Uuid (from Altinn 2 Party/UserProfile implementation will be added later)
/// </summary>
public class UserProfileLookup
{
/// <summary>
/// Gets or sets the users UserId if the lookup is to be performed based on this identifier
/// </summary>
public int UserId { get; set; }

/// <summary>
/// Gets or sets the users Username if the lookup is to be performed based on this identifier
/// </summary>
public string Username { get; set; }

/// <summary>
/// Gets or sets the users social security number or d-number from Folkeregisteret if the lookup is to be performed based on this identifier
/// </summary>
public string Ssn { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,25 @@ public async Task<UserProfile> GetUser(string ssn)

return user;
}

/// <inheritdoc/>
public async Task<UserProfile> GetUserByUsername(string username)
{
string uniqueCacheKey = "User_Username_" + username;

if (_memoryCache.TryGetValue(uniqueCacheKey, out UserProfile user))
{
return user;
}

user = await _decoratedService.GetUserByUsername(username);

if (user != null)
{
_memoryCache.Set(uniqueCacheKey, user, _cacheOptions);
}

return user;
}
}
}
23 changes: 21 additions & 2 deletions src/Altinn.Profile/Services/Implementation/UserProfilesWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
using Altinn.Platform.Profile.Models;
using Altinn.Profile.Configuration;
using Altinn.Profile.Services.Interfaces;

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

Expand Down Expand Up @@ -89,5 +87,26 @@ public async Task<UserProfile> GetUser(string ssn)

return user;
}

/// <inheritdoc />
public async Task<UserProfile> GetUserByUsername(string username)
{
UserProfile user;

Uri endpointUrl = new Uri($"{_generalSettings.BridgeApiEndpoint}users/?username={username}");

HttpResponseMessage response = await _client.GetAsync(endpointUrl);

if (!response.IsSuccessStatusCode)
{
_logger.LogError("Getting user {username} failed with {statusCode}", username, response.StatusCode);
return null;
}

string content = await response.Content.ReadAsStringAsync();
user = JsonSerializer.Deserialize<UserProfile>(content, _serializerOptions);

return user;
}
}
}
7 changes: 7 additions & 0 deletions src/Altinn.Profile/Services/Interfaces/IUserProfiles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,12 @@ public interface IUserProfiles
/// <param name="ssn">The user's ssn.</param>
/// <returns>User profile connected to given ssn.</returns>
Task<UserProfile> GetUser(string ssn);

/// <summary>
/// Method that fetches a user based on username.
/// </summary>
/// <param name="username">The user's username.</param>
/// <returns>User profile connected to given username.</returns>
Task<UserProfile> GetUserByUsername(string username);
}
}
Loading

0 comments on commit d2d226b

Please sign in to comment.