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(dashboard): Improve validation for missing permissions when creating new app #14395

Merged
merged 20 commits into from
Jan 27, 2025
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
merge main into feature
framitdavid committed Jan 14, 2025
commit 527e86a95b2ab5c5048ded96d7ff613176c587fb
6 changes: 5 additions & 1 deletion backend/src/Designer/Controllers/UserController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Altinn.Studio.Designer.Helpers;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.Models.Dto;
using Altinn.Studio.Designer.Services.Interfaces;
using Microsoft.AspNetCore.Antiforgery;
@@ -87,7 +89,9 @@ public async Task<IActionResult> PutStarred(string org, string repository)
[Route("org-permissions/{org}")]
public async Task<IActionResult> HasAccessToCreateRepository(string org)
{
UserOrgPermission userOrg = await _userService.GetUserOrgPermission(org);
string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext);
AltinnOrgContext editingContext = AltinnOrgContext.FromOrg(org, developer);
UserOrgPermission userOrg = await _userService.GetUserOrgPermission(editingContext);
return Ok(userOrg);
}

2 changes: 1 addition & 1 deletion backend/src/Designer/RepositoryClient/Model/Team.cs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ public class Team
/// </summary>
public string Name { get; set; }

public bool can_create_org_repo { get; set; }
public bool CanCreateOrgRepo { get; set; }

/// <summary>
/// The organization that owns the team
22 changes: 10 additions & 12 deletions backend/src/Designer/Services/Implementation/UserService.cs
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Altinn.Studio.Designer.Helpers;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.Models.Dto;
using Altinn.Studio.Designer.RepositoryClient.Model;
using Altinn.Studio.Designer.Services.Interfaces;
using Microsoft.AspNetCore.Http;

namespace Altinn.Studio.Designer.Services.Implementation;

public class UserService : IUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IGitea _giteaApi;

public UserService(IHttpContextAccessor httpContextAccessor, IGitea giteaApi)
public UserService(IGitea giteaApi)
{
_httpContextAccessor = httpContextAccessor;
_giteaApi = giteaApi;
}

public async Task<UserOrgPermission> GetUserOrgPermission(string org)
public async Task<UserOrgPermission> GetUserOrgPermission(AltinnOrgContext altinnOrgContext)
{
bool canCreateOrgRepo = await HasPermissionToCreateOrgRepo(org);
bool canCreateOrgRepo = await HasPermissionToCreateOrgRepo(altinnOrgContext);
return new UserOrgPermission() { CanCreateOrgRepo = canCreateOrgRepo };
}

private bool IsUserSelfOrg(string org)
private bool IsUserSelfOrg(string developerName, string org)
{
return AuthenticationHelper.GetDeveloperUserName(_httpContextAccessor.HttpContext) == org;
return developerName == org;
}

private async Task<bool> HasPermissionToCreateOrgRepo(string org)
private async Task<bool> HasPermissionToCreateOrgRepo(AltinnOrgContext altinnOrgContext)
{
List<Team> teams = await _giteaApi.GetTeams();
return IsUserSelfOrg(org) || teams.Any(team => CheckPermissionToCreateOrgRepo(team, org));
return IsUserSelfOrg(altinnOrgContext.DeveloperName, altinnOrgContext.Org) ||
teams.Any(team => CheckPermissionToCreateOrgRepo(team, altinnOrgContext.Org));
}

private static bool CheckPermissionToCreateOrgRepo(Team team, string org)
{
return team.can_create_org_repo && team.Organization.Username == org;
return team.CanCreateOrgRepo && team.Organization.Username == org;
}
}
3 changes: 2 additions & 1 deletion backend/src/Designer/Services/Interfaces/IUserService.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System.Threading.Tasks;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.Models.Dto;

namespace Altinn.Studio.Designer.Services.Interfaces;

public interface IUserService
{
public Task<UserOrgPermission> GetUserOrgPermission(string org);
public Task<UserOrgPermission> GetUserOrgPermission(AltinnOrgContext altinnOrgContext);
}
Original file line number Diff line number Diff line change
@@ -85,9 +85,12 @@ public async Task HasAccessToCreateRepository_ShouldReturnCorrectPermissions(str

response.StatusCode.Should().Be(HttpStatusCode.OK);
string content = await response.Content.ReadAsStringAsync();
var deserializeOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};

var userOrgPermission = JsonSerializer.Deserialize<UserOrgPermission>(content,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var userOrgPermission = JsonSerializer.Deserialize<Team>(content, deserializeOptions);

userOrgPermission.Should().NotBeNull();
userOrgPermission.CanCreateOrgRepo.Should().Be(expectedCanCreate);
15 changes: 6 additions & 9 deletions backend/tests/Designer.Tests/Services/UserServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.RepositoryClient.Model;
using Altinn.Studio.Designer.Services.Implementation;
using Altinn.Studio.Designer.Services.Interfaces;
@@ -11,15 +12,11 @@ namespace Designer.Tests.Services
{
public class UserServiceTests
{
private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
private readonly Mock<IGitea> _giteaApi;

public UserServiceTests()
{
_httpContextAccessor = new Mock<IHttpContextAccessor>();
_giteaApi = new Mock<IGitea>();
var context = new DefaultHttpContext();
_httpContextAccessor.Setup(req => req.HttpContext).Returns(context);
}

[Theory]
@@ -29,18 +26,18 @@ public async Task GetUserOrgPermission_ReturnsCorrectPermission(string org, bool
{
var teams = new List<Team>
{
new Team
new()
{
Organization = new Organization { Username = org },
can_create_org_repo = expectedCanCreate
Organization = new Organization { Username = org }, CanCreateOrgRepo = expectedCanCreate
}
};

_giteaApi.Setup(api => api.GetTeams()).ReturnsAsync(teams);

var userService = new UserService(_httpContextAccessor.Object, _giteaApi.Object);
var userService = new UserService(_giteaApi.Object);

var result = await userService.GetUserOrgPermission(org);
AltinnOrgContext altinnOrgContext = AltinnOrgContext.FromOrg(org, "developer");
var result = await userService.GetUserOrgPermission(altinnOrgContext);

Assert.NotNull(result);
Assert.Equal(expectedCanCreate, result.CanCreateOrgRepo);
1 change: 1 addition & 0 deletions frontend/packages/shared/src/api/queries.ts
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@ import {
optionListPath,
optionListReferencesPath,
userOrgPermissionsPath,
dataTypePath,
} from './paths';

import type { AppReleasesResponse, DataModelMetadataResponse, SearchRepoFilterParams, SearchRepositoryResponse } from 'app-shared/types/api';
1 change: 1 addition & 0 deletions frontend/packages/shared/src/types/QueryKey.ts
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ export enum QueryKey {
AppScopes = 'AppScopes',
SelectedAppScopes = 'SelectedAppScopes',
UserOrgPermissions = 'UserOrgPermissions',
DataType = 'DataType',

// Resourceadm
ResourceList = 'ResourceList',
You are viewing a condensed version of this merge commit. You can view the full changes here.