constellation-api/CH.KeycloakApi/KeycloakService.cs

182 lines
7.7 KiB
C#

using System.Text;
using IdentityModel.Client;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace CH.KeycloakApi;
public class KeycloakService
{
private readonly KeycloakSettings settings;
public KeycloakService(IConfiguration configuration)
{
this.settings = new KeycloakSettings();
configuration.Bind("Keycloak", settings);
}
public KeycloakSettings Settings => settings;
// use token manager instead
public async Task<string> GetTokenAsync(CancellationToken cancellationToken = default)
{
var tokenEndpoint = $"{this.settings.Endpoint}/realms/${settings.ApiRealm}/protocol/openid-connect/token";
var client = new HttpClient();
var response = await client.RequestTokenAsync(new TokenRequest
{
Address = tokenEndpoint,
GrantType = "client_credentials",
ClientId = this.settings.ClientId,
ClientSecret = this.settings.ClientSecret,
}, cancellationToken);
return response.AccessToken;
}
public async Task<KeycloakUser?> GetUserByEmailAsync(string realm, string email)
{
var httpClient = new HttpClient();
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users?email={email}";
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var keycloakUsers = JsonConvert.DeserializeObject<List<KeycloakUser>>(json);
return keycloakUsers?.FirstOrDefault();
}
public async Task<KeycloakUser?> GetUserByIdAsync(string realm, string id)
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users/{id}";
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var keycloakUser = JsonConvert.DeserializeObject<KeycloakUser>(json);
return keycloakUser;
}
public async Task SendChangePasswordEmailAsync(string realm, string id)
{
//PUT /{realm}/users/{id}/execute-actions-email
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users/{id}/execute-actions-email";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var putJson = JsonConvert.SerializeObject(new string[] {
"UPDATE_PASSWORD"
});
var response = await httpClient.PutAsync(url, new StringContent(putJson, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
public async Task<List<KeycloakUser>?> GetUsersAsync(string realm, string? search = null, int max = 100)
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users?max={max}";
if (!string.IsNullOrWhiteSpace(search))
url += $"&search={search}";
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var keycloakUsers = JsonConvert.DeserializeObject<List<KeycloakUser>>(json);
return keycloakUsers;
}
public async Task ChangePasswordAsync(string realm, string id, string newPassword, bool temporary)
{
// auth/admin/realms/{realm}/users/{id}/reset-password
/////{ "type": "password", "temporary": false, "value": "my-new-password" }
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users/{id}/reset-password";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var putJson = JsonConvert.SerializeObject(new {
type = "password",
temporary,
value = newPassword
});
var response = await httpClient.PutAsync(url, new StringContent(putJson, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
public async Task UpdateUserByIdAsync(string realm, string id, string email, string firstName, string lastName, bool enabled)
{
var user = await GetUserByIdAsync(realm, id);
if (user == null)
throw new Exception($"no user {email} from on realm {realm}");
user.Email = email;
user.FirstName = firstName;
user.LastName = lastName;
user.Username = email;
user.Enabled = enabled;
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users/{id}";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var putJson = JsonConvert.SerializeObject(user);
var response = await httpClient.PutAsync(url, new StringContent(putJson, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
public async Task<bool> EmailExistsAsync(string realm, string email)
{
return await this.GetUserByEmailAsync(realm, email) != null;
}
public async Task<KeycloakUser?> CreateOrUpdateAsync(string realm, string email, string firstName, string lastName, bool enabled)
{
var existingUser = await GetUserByEmailAsync(realm, email);
if (existingUser != null)
{
await UpdateUserByIdAsync(realm, existingUser.Id, email, firstName, lastName, enabled);
return await GetUserByEmailAsync(realm, email);
}
return await CreateUserAsync(realm, email, firstName, lastName, enabled);
}
public async Task<KeycloakUser?> CreateUserAsync(string realm, string email, string firstName, string lastName, bool enabled)
{
long epochTicks = new DateTime(1970, 1, 1).Ticks;
long unixTime = ((DateTime.UtcNow.Ticks - epochTicks) / TimeSpan.TicksPerSecond);
var user = new KeycloakUser()
{
CreatedTimestamp = unixTime,
Username = email,
FirstName = firstName,
LastName = lastName,
Enabled = enabled,
Totp = false,
EmailVerified = false,
RequiredActions = new List<String>(),
Attributes = null,
Email = email,
NotBefore = 0,
Access = new Dictionary<string, object>
{
{ "manageGroupMembership", true },
{ "view", true },
{ "mapRoles", true },
{ "impersonate", true },
{ "manage", true }
}
};
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", await GetTokenAsync());
var url = $"{this.settings.Endpoint}/admin/realms/{realm}/users";
var postJson = JsonConvert.SerializeObject(user);
var response = await httpClient.PostAsync(url, new StringContent(postJson, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var keycloakUser = await GetUserByEmailAsync(realm, email);
return keycloakUser;
}
}