constellation-api/CH.KeycloakApi/KeycloakService.cs

180 lines
7.6 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;
public async Task<string> GetTokenAsync()
{
var tokenEndpoint = $"{this.settings.Endpoint}/realms/master/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,
});
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 ret = JsonConvert.DeserializeObject<List<KeycloakUser>>(json);
return ret.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 ret = JsonConvert.DeserializeObject<KeycloakUser>(json);
return ret;
}
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 ret = JsonConvert.DeserializeObject<List<KeycloakUser>>(json);
return ret;
}
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 ret = await GetUserByEmailAsync(realm, email);
return ret;
}
}