Skip to content

Commit

Permalink
feat: Add IKeycloakPolicyClient
Browse files Browse the repository at this point in the history
  • Loading branch information
NikiforovAll committed May 4, 2024
1 parent 117af72 commit e04de87
Show file tree
Hide file tree
Showing 15 changed files with 1,294 additions and 267 deletions.
32 changes: 0 additions & 32 deletions src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8058,38 +8058,6 @@ public void Dispose()
}
}

[System.CodeDom.Compiler.GeneratedCode(
"NSwag",
"13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))"
)]
public partial class KeycloakHttpClientException : System.Exception
{
public int StatusCode { get; private set; }

public string HttpResponse { get; private set; }

public ErrorResponse Response { get; private set; }

public KeycloakHttpClientException(
string message,
int statusCode,
string httpResponse,
ErrorResponse response,
System.Exception? innerException
)
: base(message + "\n\nStatus: " + statusCode, innerException)
{
StatusCode = statusCode;
HttpResponse = httpResponse;
Response = response;
}

public override string ToString()
{
return string.Format("HTTP Response: \n\n{0}\n\n{1}", HttpResponse, base.ToString());
}
}

#pragma warning restore 108
#pragma warning restore 114
#pragma warning restore 472
Expand Down
1 change: 0 additions & 1 deletion src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace Keycloak.AuthServices.Sdk;

using System.Net.Http.Json;
using System.Text.Json;
using Keycloak.AuthServices.Sdk.Admin.Models;

/// <summary>
/// TBD:
Expand Down
58 changes: 58 additions & 0 deletions src/Keycloak.AuthServices.Sdk/KeycloakHttpClientException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace Keycloak.AuthServices.Sdk;

using System.Globalization;

/// <summary>
/// Represents an exception that is thrown when an HTTP request to the Keycloak fails.
/// </summary>
public partial class KeycloakHttpClientException : Exception
{
/// <summary>
/// Gets the status code of the HTTP response.
/// </summary>
public int StatusCode { get; private set; }

/// <summary>
/// Gets the raw HTTP response.
/// </summary>
public string HttpResponse { get; private set; }

/// <summary>
/// Gets the deserialized error response from the Keycloak server.
/// </summary>
public ErrorResponse Response { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="KeycloakHttpClientException"/> class with the specified parameters.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="statusCode">The status code of the HTTP response.</param>
/// <param name="httpResponse">The raw HTTP response.</param>
/// <param name="response">The deserialized error response from the Keycloak server.</param>
/// <param name="innerException">The inner exception that caused this exception, or <c>null</c> if no inner exception is specified.</param>
public KeycloakHttpClientException(
string message,
int statusCode,
string httpResponse,
ErrorResponse response,
Exception? innerException
)
: base(message + "\n\nStatus: " + statusCode, innerException)
{
this.StatusCode = statusCode;
this.HttpResponse = httpResponse;
this.Response = response;
}

/// <summary>
/// Returns a string that represents the current object, including the HTTP response.
/// </summary>
/// <returns>A string that represents the current object, including the HTTP response.</returns>
public override string ToString() =>
string.Format(
CultureInfo.InvariantCulture,
"HTTP Response: \n\n{0}\n\n{1}",
this.HttpResponse,
base.ToString()
);
}
19 changes: 16 additions & 3 deletions src/Keycloak.AuthServices.Sdk/Protection/ApiUrls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,29 @@ namespace Keycloak.AuthServices.Sdk.Protection;
/// </summary>
internal static class ApiUrls
{
#region ProtectedResource
internal const string GetResources = "/realms/{realm}/authz/protection/resource_set";

internal const string GetResource = $"{GetResources}/{{id}}";

internal const string CreateResource = $"{GetResources}";
internal const string CreateResource = GetResources;

internal const string UpdateResource = $"{GetResource}";
internal const string UpdateResource = $"{GetResources}/{{id}}";

internal const string DeleteResource = $"{GetResource}";
internal const string DeleteResource = $"{GetResources}/{{id}}";
#endregion

#region Policy
internal const string GetPolicies = "/realms/{realm}/authz/protection/uma-policy";

internal const string GetPolicy = $"{GetPolicies}/{{id}}";

internal const string CreatePolicy = $"{GetPolicies}/{{resourceId}}";

internal const string UpdatePolicy = $"{GetPolicies}/{{id}}";

internal const string DeletePolicy = $"{GetPolicies}/{{id}}";
#endregion
public static string WithRealm(this string path, string realm) =>
path.Replace("{realm}", realm);
}
209 changes: 209 additions & 0 deletions src/Keycloak.AuthServices.Sdk/Protection/IKeycloakPolicyClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
namespace Keycloak.AuthServices.Sdk.Protection;

using Keycloak.AuthServices.Sdk.Protection.Models;
using Keycloak.AuthServices.Sdk.Protection.Requests;

/// <summary>
/// Must be used by the owner of the resource for whom the policy is being created.
/// </summary>
/// <remarks>
/// https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_authorization_uma_policy_api
/// </remarks>
public interface IKeycloakPolicyClient
{
/// <summary>
/// Gets all Policies
/// </summary>
/// <remarks>
/// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-policy-api.adoc#querying-permission
/// </remarks>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="parameters"></param>
/// <param name="cancellationToken"></param>
/// <returns>The HttpResponseMessage of the request</returns>
Task<HttpResponseMessage> GetPoliciesWithResponseAsync(
string realm,
GetPoliciesRequestParameters? parameters = default,
CancellationToken cancellationToken = default
);

/// <summary>
/// Gets all Policies
/// </summary>
/// <remarks>
/// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-policy-api.adoc#querying-permission
/// </remarks>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="parameters"></param>
/// <param name="cancellationToken"></param>
/// <returns>A list of policy representations <see cref="Policy"/></returns>
async Task<IEnumerable<Policy>> GetPoliciesAsync(
string realm,
GetPoliciesRequestParameters? parameters = default,
CancellationToken cancellationToken = default
)
{
var response = await this.GetPoliciesWithResponseAsync(
realm,
parameters,
cancellationToken
);

return await response.GetResponseAsync<IEnumerable<Policy>>(cancellationToken)
?? Enumerable.Empty<Policy>();
}

/// <summary>
/// Gets a Policy
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="policyId">Policy ID</param>
/// <param name="cancellationToken"></param>
/// <returns>The HttpResponseMessage of the request</returns>
Task<HttpResponseMessage> GetPolicyWithResponseAsync(
string realm,
string policyId,
CancellationToken cancellationToken = default
);

/// <summary>
/// Get representation of a Policy
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="policyId">Policy ID</param>
/// <param name="cancellationToken"></param>
/// <returns>The policy representation <see cref="Policy"/></returns>
async Task<Policy> GetPolicyAsync(
string realm,
string policyId,
CancellationToken cancellationToken = default
)
{
var response = await this.GetPolicyWithResponseAsync(realm, policyId, cancellationToken);

return await response.GetResponseAsync<Policy>(cancellationToken) ?? new();
}

/// <summary>
/// Creates a policy
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="resourceId">The resource ID to create the policy for.</param>
/// <param name="policy">Policy representation</param>
/// <param name="cancellationToken"></param>
/// <returns>The HttpResponseMessage of the request</returns>
Task<HttpResponseMessage> CreatePolicyWithResponseAsync(
string realm,
string resourceId,
Policy policy,
CancellationToken cancellationToken
);

/// <summary>
/// Creates a policy
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="resourceId">The resource ID to create the policy for.</param>
/// <param name="policy">Policy representation</param>
/// <param name="cancellationToken"></param>
/// <returns>The created policy representation <see cref="Policy"/></returns>
async Task CreatePolicyAsync(
string realm,
string resourceId,
Policy policy,
CancellationToken cancellationToken = default
)
{
var response = await this.CreatePolicyWithResponseAsync(
realm,
resourceId,
policy,
cancellationToken
);

await response.EnsureResponseAsync(cancellationToken);
}

/// <summary>
/// Updates a policy
/// </summary>
/// <remarks>
/// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-policy-api.adoc#managing-resource-permissions-using-the-policy-api
/// </remarks>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="policyId">Policy ID.</param>
/// <param name="policy">Policy object.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<HttpResponseMessage> UpdatePolicyWithResponseAsync(
string realm,
string policyId,
Policy policy,
CancellationToken cancellationToken = default
);

/// <summary>
/// Updates a policy
/// </summary>
/// <remarks>
/// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-policy-api.adoc#managing-resource-permissions-using-the-policy-api
/// </remarks>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="policyId">Policy ID.</param>
/// <param name="policy">Policy object.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
async Task UpdatePolicyAsync(
string realm,
string policyId,
Policy policy,
CancellationToken cancellationToken = default
)
{
var response = await this.UpdatePolicyWithResponseAsync(
realm,
policyId,
policy,
cancellationToken
);

await response.EnsureResponseAsync(cancellationToken);
}

/// <summary>
/// Deletes a policy
/// </summary>
/// <remarks>
/// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-policy-api.adoc#removing-a-permission
/// </remarks>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="policyId">Policy ID.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<HttpResponseMessage> DeletePolicyWithResponseAsync(
string realm,
string policyId,
CancellationToken cancellationToken = default
);

/// <summary>
/// Deletes a policy
/// </summary>
/// <remarks>
/// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-policy-api.adoc#removing-a-permission
/// </remarks>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="policyId">Policy ID.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
async Task DeletePolicyAsync(
string realm,
string policyId,
CancellationToken cancellationToken = default
)
{
var response = await this.DeletePolicyWithResponseAsync(realm, policyId, cancellationToken);

await response.EnsureResponseAsync(cancellationToken);
}
}
Loading

0 comments on commit e04de87

Please sign in to comment.