API access with client credentials (core functionality) (#16817)

* First stab

* Delivery API client credentials + a little refactor to ensure unique client IDs

* Introduce user type

* Support user type in the Management API

* Clean up TODOs

* Update API user last login date when issuing a token

* Better error reporting for mismatched user types

* Do not allow password change or reset for API users

* Update OpenApi.json

* Revert change

* Remove obsolete comment

* Make applicable classes abstract or sealed

* Review changes

* Add endpoint for retrieving all user client IDs
This commit is contained in:
Kenn Jacobsen
2024-07-29 14:34:11 +02:00
committed by GitHub
parent 0eef280a20
commit 68db079700
53 changed files with 1444 additions and 15 deletions

View File

@@ -0,0 +1,66 @@
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class UserServiceCrudTests
{
[Test]
public async Task Can_Reset_Password()
{
var securitySettings = new SecuritySettings();
var userService = CreateUserService(securitySettings);
var userGroup = await UserGroupService.GetAsync(Constants.Security.AdminGroupAlias);
var creationModel = new UserCreateModel
{
UserName = "some@one",
Email = "some@one",
Name = "Some One",
UserGroupKeys = new HashSet<Guid> { userGroup.Key }
};
var userKey = (await userService.CreateAsync(Constants.Security.SuperUserKey, creationModel, true)).Result.CreatedUser!.Key;
var result = await userService.ResetPasswordAsync(Constants.Security.SuperUserKey, userKey);
Assert.Multiple(() =>
{
Assert.IsTrue(result.Success);
Assert.AreEqual(UserOperationStatus.Success, result.Status);
Assert.IsNotNull(result.Result.ResetPassword);
});
}
[Test]
public async Task Cannot_Reset_Password_For_Api_User()
{
var securitySettings = new SecuritySettings();
var userService = CreateUserService(securitySettings);
var userGroup = await UserGroupService.GetAsync(Constants.Security.AdminGroupAlias);
var creationModel = new UserCreateModel
{
UserName = "some@one",
Email = "some@one",
Name = "Some One",
UserGroupKeys = new HashSet<Guid> { userGroup.Key },
Type = UserType.Api
};
var userKey = (await userService.CreateAsync(Constants.Security.SuperUserKey, creationModel, true)).Result.CreatedUser!.Key;
var result = await userService.ResetPasswordAsync(Constants.Security.SuperUserKey, userKey);
Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.AreEqual(UserOperationStatus.InvalidUserType, result.Status);
Assert.IsNull(result.Result.ResetPassword);
Assert.IsNull(result.Exception);
});
}
}

View File

@@ -50,6 +50,37 @@ public partial class UserServiceCrudTests
Assert.IsNotNull(createdUser);
Assert.AreEqual(username, createdUser.Username);
Assert.AreEqual(email, createdUser.Email);
Assert.AreEqual(UserType.Default, createdUser.Type);
}
[TestCase(UserType.Default)]
[TestCase(UserType.Api)]
public async Task Can_Create_All_User_Types(UserType type)
{
var securitySettings = new SecuritySettings();
var userService = CreateUserService(securitySettings);
var userGroup = await UserGroupService.GetAsync(Constants.Security.AdminGroupAlias);
var creationModel = new UserCreateModel
{
UserName = "api@local",
Email = "api@local",
Name = "API user",
UserGroupKeys = new HashSet<Guid> { userGroup.Key },
Type = type
};
var result = await userService.CreateAsync(Constants.Security.SuperUserKey, creationModel, true);
Assert.IsTrue(result.Success);
Assert.AreEqual(UserOperationStatus.Success, result.Status);
var createdUser = result.Result.CreatedUser;
Assert.IsNotNull(createdUser);
Assert.AreEqual(type, createdUser.Type);
var user = await userService.GetAsync(createdUser.Key);
Assert.NotNull(user);
Assert.AreEqual(type, user.Type);
}
[Test]