diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeAgentPool_v1.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeAgentPool_v1.cs index 8612b1e9..7afbd838 100644 --- a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeAgentPool_v1.cs +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeAgentPool_v1.cs @@ -1,14 +1,11 @@ -using System.Net.Http.Headers; using System.Text.Json; -using Microsoft.TeamFoundation.Build.WebApi; -using Microsoft.VisualStudio.Services.WebApi; using Nox.Cli.Abstractions; using Nox.Cli.Abstractions.Exceptions; using Nox.Cli.Abstractions.Extensions; using Nox.Cli.Abstractions.Helpers; +using Nox.Cli.Plugin.AzDevOps.Clients; using Nox.Cli.Plugin.AzDevOps.DTO; using RestSharp; -using RestSharp.Authenticators; namespace Nox.Cli.Plugin.AzDevOps; @@ -90,38 +87,8 @@ public async Task> ProcessAsync(INoxWorkflowContext { try { - var client = new RestClient(_server); - - var request = new RestRequest($"/{_projectId}/_apis/pipelines/pipelinePermissions/queue/{_queueId}") - { - Method = Method.Patch - }; - var base64Token = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($":{_pat}")); - - request.AddHeader("Authorization", $"Basic {base64Token}"); - request.AddHeader("Content-Type", "application/json"); - request.AddHeader("Accept", "application/json;api-version=5.1-preview.1"); - var payload = new AuthorizeRequest() - { - Resource = new Resource - { - Type = "queue", - Id = _queueId.ToString() - }, - AllPipelines = new PipelineAuthorizeAll - { - Authorized = true - } - }; - request.AddJsonBody(JsonSerializer.Serialize(payload, JsonOptions.Instance)); - var response = await client.ExecuteAsync(request); - if (response.IsSuccessStatusCode) - { - ctx.SetState(ActionState.Success); - return outputs; - } - - throw new NoxCliException(response.Content!); + var client = new PipelineClient(_server, _pat); + await client.AuthorizeAgentQueuePipelines(_projectId.Value, _queueId!.Value); } catch (Exception ex) { diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeServiceEndpoint_v1.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeServiceEndpoint_v1.cs index 479fd5a3..341b55d9 100644 --- a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeServiceEndpoint_v1.cs +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevOpsAuthorizeServiceEndpoint_v1.cs @@ -3,6 +3,7 @@ using Nox.Cli.Abstractions.Exceptions; using Nox.Cli.Abstractions.Extensions; using Nox.Cli.Abstractions.Helpers; +using Nox.Cli.Plugin.AzDevOps.Clients; using Nox.Cli.Plugin.AzDevOps.DTO; using RestSharp; @@ -97,37 +98,8 @@ public async Task> ProcessAsync(INoxWorkflowContext { try { - var client = new RestClient(_server); - - var request = new RestRequest($"/{_projectId}/_apis/pipelines/pipelinePermissions/endpoint/{_endpointId}") - { - Method = Method.Patch - }; - var base64Token = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($":{_pat}")); - - request.AddHeader("Authorization", $"Basic {base64Token}"); - request.AddHeader("Content-Type", "application/json"); - request.AddHeader("Accept", "application/json;api-version=5.1-preview.1"); - var payload = new AuthorizeRequest() - { - Pipelines = new List - { - new PipelineAuthorize - { - Id = _pipelineId!.Value, - Authorized = true - } - } - }; - request.AddJsonBody(JsonSerializer.Serialize(payload, JsonOptions.Instance)); - var response = await client.ExecuteAsync(request); - if (response.IsSuccessStatusCode) - { - ctx.SetState(ActionState.Success); - return outputs; - } - - throw new NoxCliException(response.Content!); + var client = new PipelineClient(_server, _pat); + await client.AuthorizeEndpointPipeline(_projectId.Value, _endpointId.Value, _pipelineId!.Value); } catch (Exception ex) { diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevopsAddProjectAadGroup_v1.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevopsAddProjectAadGroup_v1.cs new file mode 100644 index 00000000..ddd6e815 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/AzDevopsAddProjectAadGroup_v1.cs @@ -0,0 +1,153 @@ +using Nox.Cli.Abstractions; +using Nox.Cli.Abstractions.Extensions; +using Nox.Cli.Plugin.AzDevOps.Clients; +using Nox.Cli.Plugin.AzDevOps.Enums; + +namespace Nox.Cli.Plugin.AzDevOps; + +public class AzDevopsAddProjectAadGroup_v1 : INoxCliAddin +{ + public NoxActionMetaData Discover() + { + return new NoxActionMetaData + { + Name = "azdevops/add-project-aad-group@v1", + Author = "Jan Schutte", + Description = "Add an AAD group to a DevOps project group", + + Inputs = + { + ["server"] = new NoxActionInput { + Id = "server", + Description = "The DevOps server hostname or IP", + Default = "localhost", + IsRequired = true + }, + + ["personal-access-token"] = new NoxActionInput { + Id = "personal-access-token", + Description = "The personal access token to connect to DevOps with", + Default = string.Empty, + IsRequired = true + }, + ["project-id"] = new NoxActionInput + { + Id = "project-id", + Description = "The DevOps project Id", + Default = Guid.Empty, + IsRequired = true + }, + ["project-group-name"] = new NoxActionInput + { + Id = "project-group-name", + Description = "The DevOps project group to add the AAD group to", + Default = string.Empty, + IsRequired = true + }, + ["aad-group-name"] = new NoxActionInput + { + Id = "aad-group-name", + Description = "The AAD group to add", + Default = Guid.Empty, + IsRequired = true + } + } + }; + } + + private string? _server; + private string? _pat; + private Guid? _projectId; + private string? _projectGroupName; + private string? _aadGroupName; + private bool _isServerContext = false; + + public Task BeginAsync(IDictionary inputs) + { + _server = inputs.Value("server"); + _pat = inputs.Value("personal-access-token"); + _projectId = inputs.Value("project-id"); + _aadGroupName = inputs.Value("project-group-name"); + _aadGroupName = inputs.Value("aad-group-name"); + return Task.CompletedTask; + } + + public async Task> ProcessAsync(INoxWorkflowContext ctx) + { + _isServerContext = ctx.IsServer; + var outputs = new Dictionary(); + + ctx.SetState(ActionState.Error); + + if (string.IsNullOrWhiteSpace(_server) || + string.IsNullOrWhiteSpace(_pat) || + _projectId == null || + _projectId == Guid.Empty || + string.IsNullOrEmpty(_projectGroupName) || + string.IsNullOrEmpty(_aadGroupName)) + { + ctx.SetErrorMessage("The devops add-project-aad-group action was not initialized"); + } + else + { + try + { + var result = await AddAdmins(ctx); + if (result) + { + ctx.SetState(ActionState.Success); + } + } + catch (Exception ex) + { + ctx.SetErrorMessage(ex.Message); + } + } + + return outputs; + } + + public Task EndAsync() + { + return Task.CompletedTask; + } + + private async Task AddAdmins(INoxWorkflowContext ctx) + { + var identityPickerClient = new IdentityPickerClient(_server!, _pat!); + var graphClient = new GraphClient(_server!, _pat!); + + var aadGroups = await identityPickerClient.FindIdentity(_aadGroupName!, IdentityType.Group); + if (aadGroups == null) + { + ctx.SetErrorMessage($"Unable to locate the AAD group: {_aadGroupName}"); + return false; + } + + var aadGroup = aadGroups.First(); + + + var projectDescriptor = await graphClient.GetDescriptor(_projectId.ToString()!); + if (!_projectGroupName!.StartsWith('\\')) _projectGroupName = '\\' + _projectGroupName; + var projectGroup = await graphClient.FindProjectGroup(projectDescriptor!, _projectGroupName); + if (projectGroup == null) + { + ctx.SetErrorMessage($"Unable to locate the project administrator group for this DevOps project"); + return false; + } + + var aadGroupDescriptor = await graphClient.GetDescriptor(projectGroup.Descriptor!, aadGroup.OriginId!); + + if (string.IsNullOrEmpty(aadGroupDescriptor)) + { + ctx.SetErrorMessage($"Unable to retrieve AAD group descriptor."); + return false; + } + + return await graphClient.AddGroupMembership(projectGroup.Descriptor!, aadGroupDescriptor!); + + + } + + +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/GraphClient.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/GraphClient.cs new file mode 100644 index 00000000..1d0bfb9a --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/GraphClient.cs @@ -0,0 +1,118 @@ +using System.Text.Json; +using Microsoft.VisualStudio.Services.Graph.Client; +using Nox.Cli.Abstractions.Helpers; +using Nox.Cli.Plugin.AzDevOps.DTO; +using Nox.Cli.Plugin.AzDevOps.Exceptions; +using Nox.Cli.Plugin.AzDevOps.Helpers; +using RestSharp; + +namespace Nox.Cli.Plugin.AzDevOps.Clients; + +public class GraphClient +{ + private readonly RestClient _client; + private readonly string _pat; + + + public GraphClient(string serverUri, string pat) + { + var builder = new UriBuilder(new Uri(serverUri)); + builder.Host = $"vssps.{builder.Host}"; + serverUri = builder.Uri.ToString(); + + _client = new RestClient(serverUri); + _pat = pat; + } + + public async Task GetDescriptor(string storageKey) + { + var request = new RestRequest($"/_apis/graph/descriptors/{storageKey}") + { + Method = Method.Get + }; + + AddHeaders(request); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new DevOpsClientException($"Unable to find Descriptor for storage key: {storageKey}"); + } + + return response.Data!.Value!; + } + + public async Task GetDescriptor(string projectGroupDescriptor, string originId) + { + var request = new RestRequest($"/_apis/graph/groups") + { + Method = Method.Post + }; + request.AddQueryParameter("groupDescriptors", projectGroupDescriptor); + var payload = new AadDescriptorRequest + { + OriginId = originId + }; + request.AddJsonBody(JsonSerializer.Serialize(payload, JsonOptions.Instance)); + AddHeaders(request); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new DevOpsClientException($"Unable to find Descriptor for OriginId: {originId}, StorageKey:"); + } + + return response.Data!.Descriptor!; + } + + public async Task FindProjectGroup(string projectDescriptor, string query) + { + var request = new RestRequest($"/_apis/graph/groups") + { + Method = Method.Get + }; + request.AddQueryParameter("scopeDescriptor", projectDescriptor); + AddHeaders(request); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new DevOpsClientException($"Unable to find group: {query} ({response.ErrorMessage})"); + } + + foreach (var group in response.Data!.Value!) + { + if (group.PrincipalName!.Contains(query, StringComparison.OrdinalIgnoreCase)) + { + return group; + } + } + + return null; + } + + public async Task AddGroupMembership(string projectGroupDescriptor, string memberDescriptor) + { + var request = new RestRequest($"/_apis/Graph/Memberships/{memberDescriptor}/{projectGroupDescriptor}") + { + Method = Method.Put + }; + AddHeaders(request); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new DevOpsClientException($"An error occurred while trying to add project group membership ({response.ErrorMessage})"); + } + + if (response.Data!.ContainerDescriptor == projectGroupDescriptor && response.Data.MemberDescriptor == memberDescriptor) + { + return true; + } + + return false; + } + + private void AddHeaders(RestRequest request) + { + request.AddHeader("Authorization", $"Basic {_pat.ToEncoded()}"); + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json;api-version=7.1-preview.1"); + } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/IdentityPickerClient.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/IdentityPickerClient.cs new file mode 100644 index 00000000..a1ae7c76 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/IdentityPickerClient.cs @@ -0,0 +1,74 @@ +using System.Text.Json; +using Nox.Cli.Abstractions.Exceptions; +using Nox.Cli.Abstractions.Helpers; +using Nox.Cli.Plugin.AzDevOps.DTO; +using Nox.Cli.Plugin.AzDevOps.Enums; +using Nox.Cli.Plugin.AzDevOps.Helpers; +using RestSharp; + +namespace Nox.Cli.Plugin.AzDevOps.Clients; + +public class IdentityPickerClient +{ + private readonly RestClient _client; + private readonly string _pat; + + + public IdentityPickerClient(string serverUri, string pat) + { + _client = new RestClient(serverUri); + _pat = pat; + } + + public async Task?> FindIdentity(string query, IdentityType type) + { + var request = new RestRequest($"/_apis/identityPicker/identities") + { + Method = Method.Post + }; + AddHeaders(request); + var payload = new IdentityPickerRequest + { + Query = query, + OperationScopes = new List + { + "ims", "source" + }, + IdentityTypes = new List + { + ResolveIdentityType(type) + }, + Options = new IdentityPickerOptions + { + MinResults = 1, + MaxResults = 10 + } + }; + request.AddJsonBody(JsonSerializer.Serialize(payload, JsonOptions.Instance)); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new NoxCliException($"Unable to find identity: {response}"); + } + + return response.Data!.Results![0].Identities; + } + + private void AddHeaders(RestRequest request) + { + request.AddHeader("Authorization", $"Basic {_pat.ToEncoded()}"); + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json;api-version=5.1-preview.1"); + } + + private string ResolveIdentityType(IdentityType type) + { + switch (type) + { + case IdentityType.Group: + return "group"; + default: + return ""; + } + } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/PipelineClient.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/PipelineClient.cs new file mode 100644 index 00000000..c1238235 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Clients/PipelineClient.cs @@ -0,0 +1,84 @@ +using System.Text.Json; +using Nox.Cli.Abstractions.Helpers; +using Nox.Cli.Plugin.AzDevOps.DTO; +using Nox.Cli.Plugin.AzDevOps.Exceptions; +using Nox.Cli.Plugin.AzDevOps.Helpers; +using RestSharp; + +namespace Nox.Cli.Plugin.AzDevOps.Clients; + +public class PipelineClient +{ + private readonly RestClient _client; + private readonly string _pat; + + + public PipelineClient(string serverUri, string pat) + { + _client = new RestClient(serverUri); + _pat = pat; + } + + public async Task AuthorizeAgentQueuePipelines(Guid projectId, int queueId) + { + var request = new RestRequest($"/{projectId}/_apis/pipelines/pipelinePermissions/queue/{queueId}") + { + Method = Method.Patch + }; + AddHeaders(request); + var payload = new AuthorizeRequest() + { + Resource = new Resource + { + Type = "queue", + Id = queueId.ToString() + }, + AllPipelines = new PipelineAuthorizeAll + { + Authorized = true + } + }; + request.AddJsonBody(JsonSerializer.Serialize(payload, JsonOptions.Instance)); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new DevOpsClientException($"An error occurred while trying to authorize the pipelines on a project ({response.ErrorMessage})"); + } + } + + public async Task AuthorizeEndpointPipeline(Guid projectId, Guid endpointId, int pipelineId) + { + var request = new RestRequest($"/{projectId}/_apis/pipelines/pipelinePermissions/endpoint/{endpointId}") + { + Method = Method.Patch + }; + AddHeaders(request); + var payload = new AuthorizeRequest() + { + Pipelines = new List + { + new PipelineAuthorize + { + Id = pipelineId, + Authorized = true + } + } + }; + request.AddJsonBody(JsonSerializer.Serialize(payload, JsonOptions.Instance)); + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new DevOpsClientException($"An error occurred while trying to authorize the pipelines on a project ({response.ErrorMessage})"); + } + } + + + + private void AddHeaders(RestRequest request) + { + request.AddHeader("Authorization", $"Basic {_pat.ToEncoded()}"); + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json;api-version=5.1-preview.1"); + } + +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/AadDescriptorRequest.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/AadDescriptorRequest.cs new file mode 100644 index 00000000..b6d40360 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/AadDescriptorRequest.cs @@ -0,0 +1,7 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class AadDescriptorRequest +{ + public string? OriginId { get; set; } + public string? StorageKey { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/AadDescriptorResponse.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/AadDescriptorResponse.cs new file mode 100644 index 00000000..60a7eef3 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/AadDescriptorResponse.cs @@ -0,0 +1,6 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class AadDescriptorResponse +{ + public string? Descriptor { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/DescriptorResponse.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/DescriptorResponse.cs new file mode 100644 index 00000000..85c9829c --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/DescriptorResponse.cs @@ -0,0 +1,6 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class DescriptorResponse +{ + public string? Value { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/GraphGroupPagedResponse.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/GraphGroupPagedResponse.cs new file mode 100644 index 00000000..0bfde41a --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/GraphGroupPagedResponse.cs @@ -0,0 +1,8 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class GraphGroupPagedResponse +{ + public string? ContinuationToken { get; set; } + public int Count { get; set; } + public List? Value { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/GraphGroupResult.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/GraphGroupResult.cs new file mode 100644 index 00000000..9941aea0 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/GraphGroupResult.cs @@ -0,0 +1,12 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class GraphGroupResult +{ + public string? SubjectKind { get; set; } + public string? Description { get; set; } + public string? PrincipalName { get; set; } + public string? Origin { get; set; } + public string? OriginId { get; set; } + public string? DisplayName { get; set; } + public string? Descriptor { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/MembershipResponse.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/MembershipResponse.cs new file mode 100644 index 00000000..b03e9bd1 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Graph/MembershipResponse.cs @@ -0,0 +1,7 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class MembershipResponse +{ + public string? ContainerDescriptor { get; set; } + public string? MemberDescriptor { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerIdentity.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerIdentity.cs new file mode 100644 index 00000000..b603c4d3 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerIdentity.cs @@ -0,0 +1,14 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class IdentityPickerIdentity +{ + public string? EntityId { get; set; } + public string? EntityType { get; set; } + public string? OriginDirectory { get; set; } + public string? OriginId { get; set; } + public string? LocalDirectory { get; set; } + public string? LocalId { get; set; } + public string? DisplayName { get; set; } + public string? ScopeName { get; set; } + public string? SubjectDescriptor { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerOptions.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerOptions.cs new file mode 100644 index 00000000..c334edc6 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerOptions.cs @@ -0,0 +1,7 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class IdentityPickerOptions +{ + public int MinResults { get; set; } + public int MaxResults { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerRequest.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerRequest.cs new file mode 100644 index 00000000..94353def --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerRequest.cs @@ -0,0 +1,10 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class IdentityPickerRequest +{ + public string? Query { get; set; } + public List? IdentityTypes { get; set; } + public List? OperationScopes { get; set; } + public IdentityPickerOptions? Options { get; set; } + public List? Properties { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerResponse.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerResponse.cs new file mode 100644 index 00000000..736e9238 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerResponse.cs @@ -0,0 +1,6 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class IdentityPickerResponse +{ + public List? Results { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerResult.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerResult.cs new file mode 100644 index 00000000..d391be99 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/IdentityPicker/IdentityPickerResult.cs @@ -0,0 +1,7 @@ +namespace Nox.Cli.Plugin.AzDevOps.DTO; + +public class IdentityPickerResult +{ + public string? QueryToken { get; set; } + public List? Identities { get; set; } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/AuthorizeRequest.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/AuthorizeRequest.cs similarity index 100% rename from src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/AuthorizeRequest.cs rename to src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/AuthorizeRequest.cs diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/AuthorizeResponse.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/AuthorizeResponse.cs similarity index 100% rename from src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/AuthorizeResponse.cs rename to src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/AuthorizeResponse.cs diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/PipelineAuthorize.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/PipelineAuthorize.cs similarity index 100% rename from src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/PipelineAuthorize.cs rename to src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/PipelineAuthorize.cs diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/PipelineAuthorizeAll.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/PipelineAuthorizeAll.cs similarity index 100% rename from src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/PipelineAuthorizeAll.cs rename to src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/PipelineAuthorizeAll.cs diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/PipelineAuthorizeBy.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/PipelineAuthorizeBy.cs similarity index 100% rename from src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/PipelineAuthorizeBy.cs rename to src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/DTO/Pipeline/PipelineAuthorizeBy.cs diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Enums/IdentityType.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Enums/IdentityType.cs new file mode 100644 index 00000000..ce0b36df --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Enums/IdentityType.cs @@ -0,0 +1,7 @@ +namespace Nox.Cli.Plugin.AzDevOps.Enums; + +public enum IdentityType +{ + Group + +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Exceptions/DevOpsClientException.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Exceptions/DevOpsClientException.cs new file mode 100644 index 00000000..79fb6438 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Exceptions/DevOpsClientException.cs @@ -0,0 +1,15 @@ +namespace Nox.Cli.Plugin.AzDevOps.Exceptions; + +[Serializable] +public class DevOpsClientException: Exception +{ + public DevOpsClientException(string message): base(message) + { + + } + + public DevOpsClientException(string message, Exception innerException): base(message, innerException) + { + + } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Helpers/TokenHelper.cs b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Helpers/TokenHelper.cs new file mode 100644 index 00000000..96a86783 --- /dev/null +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Helpers/TokenHelper.cs @@ -0,0 +1,9 @@ +namespace Nox.Cli.Plugin.AzDevOps.Helpers; + +public static class TokenHelper +{ + public static string ToEncoded(this string pat) + { + return Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($":{pat}")); + } +} \ No newline at end of file diff --git a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Nox.Cli.Plugin.AzDevOps.csproj b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Nox.Cli.Plugin.AzDevOps.csproj index 88a7e1bf..3da0ab72 100755 --- a/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Nox.Cli.Plugin.AzDevOps.csproj +++ b/src/Nox.Cli.Plugins/Nox.Cli.Plugin.AzDevOps/Nox.Cli.Plugin.AzDevOps.csproj @@ -10,6 +10,7 @@ +