/// <inheritdoc/>
        public async Task AcceptRequestAsync(string requestId,
                                             VaultOperationContextModel context, CancellationToken ct)
        {
            var result = await _repo.UpdateAsync(requestId, request => {
                switch (request.Record.State)
                {
                case CertificateRequestState.New:
                case CertificateRequestState.Approved:
                case CertificateRequestState.Rejected:
                case CertificateRequestState.Failure:
                case CertificateRequestState.Completed:
                    request.Record.State    = CertificateRequestState.Accepted;
                    request.Record.Accepted = context.Validate();
                    return(true);

                case CertificateRequestState.Accepted:
                    return(false);

                default:
                    throw new ResourceInvalidStateException(
                        $"Request in the wrong state ({request.Record.State}.");
                }
            }, ct);

            await _broker.NotifyAllAsync(
                l => l.OnCertificateRequestAcceptedAsync(result));
        }
        /// <inheritdoc/>
        public async Task <StartSigningRequestResultModel> StartSigningRequestAsync(
            StartSigningRequestModel request, VaultOperationContextModel context,
            CancellationToken ct)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (string.IsNullOrEmpty(request.EntityId))
            {
                throw new ArgumentNullException(nameof(request.EntityId));
            }
            if (string.IsNullOrEmpty(request.GroupId))
            {
                throw new ArgumentNullException(nameof(request.GroupId));
            }

            var entity = await _entities.FindEntityAsync(request.EntityId);

            if (entity == null)
            {
                throw new ResourceNotFoundException("Entity not found");
            }

            // Validate signing request and update entity information
            var signingRequest = request.ToRawData();
            var info           = signingRequest.ToCertificationRequest();
            var altNames       = info.Extensions?
                                 .OfType <X509SubjectAltNameExtension>()
                                 .SingleOrDefault();

            if (!(entity.Uris?.All(
                      u => altNames?.Uris?.Any(x => u.EqualsIgnoreCase(x)) ?? false)
                  ?? true))
            {
                throw new ArgumentException(
                          "Signing Request's alternative names does not include entity's uris");
            }

            var domainNames = new HashSet <string>(entity.Addresses ?? new List <string>());

            if (altNames?.DomainNames != null)
            {
                foreach (var name in altNames.DomainNames)
                {
                    domainNames.Add(name);
                }
            }
            if (altNames?.IPAddresses != null)
            {
                foreach (var name in altNames.IPAddresses)
                {
                    domainNames.Add(name);
                }
            }
            var uris = new HashSet <string>(entity.Uris ?? new List <string>());

            if (altNames?.Uris != null)
            {
                foreach (var name in altNames.Uris)
                {
                    uris.Add(name);
                }
            }

            entity.Addresses   = domainNames.ToList();
            entity.Uris        = uris.ToList();
            entity.SubjectName = info.Subject.Name;

            var result = await _repo.AddAsync(new CertificateRequestModel {
                Record = new CertificateRequestRecordModel {
                    Type      = CertificateRequestType.KeyPairRequest,
                    EntityId  = entity.Id,
                    GroupId   = request.GroupId,
                    Submitted = context.Validate(),
                },
                Entity         = entity.Validate(),
                SigningRequest = signingRequest
            }, ct);

            await _broker.NotifyAllAsync(
                l => l.OnCertificateRequestSubmittedAsync(result));

            _logger.Information("New signing request submitted.");
            return(new StartSigningRequestResultModel {
                RequestId = result.Record.RequestId
            });
        }
        /// <inheritdoc/>
        public async Task<StartNewKeyPairRequestResultModel> StartNewKeyPairRequestAsync(
            StartNewKeyPairRequestModel request, VaultOperationContextModel context,
            CancellationToken ct) {

            if (request == null) {
                throw new ArgumentNullException(nameof(request));
            }
            if (string.IsNullOrEmpty(request.EntityId)) {
                throw new ArgumentNullException(nameof(request.EntityId));
            }
            if (string.IsNullOrEmpty(request.GroupId)) {
                throw new ArgumentNullException(nameof(request.GroupId));
            }
            if (string.IsNullOrEmpty(request.SubjectName)) {
                throw new ArgumentNullException(nameof(request.SubjectName));
            }
            // Get entity
            var entity = await _entities.FindEntityAsync(request.EntityId);
            if (entity == null) {
                throw new ResourceNotFoundException("Entity not found");
            }

            // Validate subject name
            var subjectList = Opc.Ua.Utils.ParseDistinguishedName(request.SubjectName);
            if (subjectList == null ||
                subjectList.Count == 0) {
                throw new ArgumentException("Invalid Subject", nameof(request.SubjectName));
            }
            if (!subjectList.Any(c => c.StartsWith("CN=", StringComparison.InvariantCulture))) {
                throw new ArgumentException("Invalid Subject, must have a common name (CN=).",
                    nameof(request.SubjectName));
            }
            entity.SubjectName = string.Join(", ", subjectList);

            // Add domain names
            if (request.DomainNames != null) {
                if (entity.Addresses == null) {
                    entity.Addresses = request.DomainNames;
                }
                else {
                    entity.Addresses.AddRange(request.DomainNames);
                }
            }

            var result = await _repo.AddAsync(new CertificateRequestModel {
                Record = new CertificateRequestRecordModel {
                    Type = CertificateRequestType.KeyPairRequest,
                    EntityId = entity.Id,
                    GroupId = request.GroupId,
                    Submitted = context.Validate(),
                },
                Entity = entity
            }, ct);
            await _broker.NotifyAllAsync(
                l => l.OnCertificateRequestSubmittedAsync(result));

            _logger.Information("New Key pair request submitted.");
            return new StartNewKeyPairRequestResultModel {
                RequestId = result.Record.RequestId
            };
        }