context: /*
   * Copyright 2015 Bert Hoorne,Dominick Baier, Brock Allen
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  using System;
  using System.Collections.Generic;
  using System.Data.Entity;
  using System.Data.SqlClient;
  using System.Linq;
  using System.Threading.Tasks;
  using AutoMapper;
  using IdentityServer3.Core.Models;
  using IdentityServer3.EntityFramework;
  using IdentityServer3.EntityFramework.Entities;
  using Sahra.MutualFund.IdentityAdmin.Core;
  using Sahra.MutualFund.IdentityAdmin.Core.Core.Client;
  using Sahra.MutualFund.IdentityAdmin.Core.Core.Metadata;
  using Sahra.MutualFund.IdentityAdmin.Core.Core.Scope;
  using Sahra.MutualFund.IdentityAdmin.Core.Extensions;
  using Sahra.MutualFund.IdentityAdmin.EntityFramework.Entities;
  using Sahra.MutualFund.IdentityAdmin.EntityFramework.Interfaces;
  using Client = IdentityServer3.EntityFramework.Entities.Client;
  using Scope = IdentityServer3.EntityFramework.Entities.Scope;
  using ScopeClaim = IdentityServer3.EntityFramework.Entities.ScopeClaim;
  
  namespace Sahra.MutualFund.IdentityAdmin.EntityFramework
  {
      public class IdentityAdminCoreManager<TCLient, TClientKey, TScope, TScopeKey> : IIdentityAdminService
          where TCLient : class, IClient<TClientKey>, new()
          where TClientKey : IEquatable<TClientKey>
          where TScope : class, IScope<TScopeKey>, new()
          where TScopeKey : IEquatable<TScopeKey>
      {
          private readonly EntityFrameworkServiceOptions _entityFrameworkServiceOptions;
          private static IMapper _clientMapper;
          public IdentityAdminCoreManager(string connectionString, string schema = null, bool createIfNotExist = false)
          {
              _entityFrameworkServiceOptions = new EntityFrameworkServiceOptions()
              {
                  ConnectionString = connectionString,
                  Schema = schema
              };
  
              if (createIfNotExist)
              {
                  
              }
  
              if (string.IsNullOrWhiteSpace(_entityFrameworkServiceOptions.ConnectionString))
              {
                  throw new ArgumentException("A connectionstring or name is needed to initialize the IdentityAdmin");
              }
  
              var clientConfig = new MapperConfiguration(cfg => {
                  cfg.CreateMap<IdentityClient, Client>();
                  cfg.CreateMap<Client, IdentityClient>();
                  cfg.CreateMap<ClientClaim, ClientClaimValue>();
                  cfg.CreateMap<ClientClaimValue, ClientClaim>();
                  cfg.CreateMap<ClientSecret, ClientSecretValue>();
                  cfg.CreateMap<ClientSecretValue, ClientSecret>();
                  cfg.CreateMap<ClientIdPRestriction, ClientIdPRestrictionValue>();
                  cfg.CreateMap<ClientIdPRestrictionValue, ClientIdPRestriction>();
                  cfg.CreateMap<ClientPostLogoutRedirectUri, ClientPostLogoutRedirectUriValue>();
                  cfg.CreateMap<ClientPostLogoutRedirectUriValue, ClientPostLogoutRedirectUri>();
                  cfg.CreateMap<ClientRedirectUri, ClientRedirectUriValue>();
                  cfg.CreateMap<ClientRedirectUriValue, ClientRedirectUri>();
                  cfg.CreateMap<ClientCorsOrigin, ClientCorsOriginValue>();
                  cfg.CreateMap<ClientCorsOriginValue, ClientCorsOrigin>();
                  cfg.CreateMap<ClientCustomGrantType, ClientCustomGrantTypeValue>();
                  cfg.CreateMap<ClientCustomGrantTypeValue, ClientCustomGrantType>();
                  cfg.CreateMap<ClientScope, ClientScopeValue>();
                  cfg.CreateMap<ClientScopeValue, ClientScope>();
                  cfg.CreateMap<ScopeClaim, ScopeClaimValue>();
                  cfg.CreateMap<ScopeClaimValue, ScopeClaim>();
                  cfg.CreateMap<ScopeSecret, ScopeSecretValue>();
                  cfg.CreateMap<ScopeSecretValue, ScopeSecret>();
                  cfg.CreateMap<IdentityScope, Scope>();
                  cfg.CreateMap<Scope, IdentityScope>();
                  cfg.CreateMap<DateTime?, DateTimeOffset?>().ConvertUsing<NullableDateTimeOffsetConverter>();
                  cfg.CreateMap<DateTimeOffset?, DateTime?>().ConvertUsing<NullableOffsetDateTimeConverter>();
              });
              _clientMapper = clientConfig.CreateMapper();
          }
  
          public Task<IdentityAdminMetadata> GetMetadataAsync()
          {
              var updateClient = new List<PropertyMetadata>();
              updateClient.AddRange(PropertyMetadata.FromType<TCLient>());
  
              var createClient = new List<PropertyMetadata>
              {
                  PropertyMetadata.FromProperty<TCLient>(x => x.ClientName,"ClientName", required: true),
                  PropertyMetadata.FromProperty<TCLient>(x => x.ClientId,"ClientId", required: true),
              };
  
              var client = new ClientMetaData
              {
                  SupportsCreate = true,
                  SupportsDelete = true,
                  CreateProperties = createClient,
                  UpdateProperties = updateClient
              };
  
  
              var updateScope = new List<PropertyMetadata>();
              updateScope.AddRange(PropertyMetadata.FromType<TScope>());
  
              var createScope = new List<PropertyMetadata>
              {
                  PropertyMetadata.FromProperty<TScope>(x => x.Name,"ScopeName", required: true),
              };
  
              var scope = new ScopeMetaData
              {
                  SupportsCreate = true,
                  SupportsDelete = true,
                  CreateProperties = createScope,
                  UpdateProperties = updateScope
              };
  
  
              var identityAdminMetadata = new IdentityAdminMetadata
              {
                  ClientMetaData = client,
                  ScopeMetaData = scope
              };
  
              return Task.FromResult(identityAdminMetadata);
          }
  
          #region Scope
  
          public async Task<IdentityAdminResult<ScopeDetail>> GetScopeAsync(string subject)
          {
              using (var db = new ScopeConfigurationDbContext(_entityFrameworkServiceOptions.ConnectionString, _entityFrameworkServiceOptions.Schema))
              {
                  int parsedId;
                  if (int.TryParse(subject, out parsedId))
                  {
                      var efScope = await db.Scopes.FirstOrDefaultAsync(p => p.Id == parsedId);
                      if (efScope == null)
                      {
                          return new IdentityAdminResult<ScopeDetail>((ScopeDetail)null);
                      }
                      var coreScope = new TScope();
                      _clientMapper.Map(efScope, coreScope);
                      var result = new ScopeDetail
                      {
                          Subject = subject,
                          Name = efScope.Name,
                          Description = efScope.Description,
                      };
  
                      var metadata = await GetMetadataAsync();
                      var props = from prop in metadata.ScopeMetaData.UpdateProperties
                                  select new PropertyValue
                                  {
                                      Type = prop.Type,
                                      Value = GetScopeProperty(prop, coreScope),
                                  };
  
                      result.Properties = props.ToArray();
                      var scopeClaimValues = new List<ScopeClaimValue>();
                      var scopeSecrets = new List<ScopeSecretValue>();
                    
                      _clientMapper.Map(efScope.ScopeClaims.ToList(), scopeClaimValues);
                      _clientMapper.Map(efScope.ScopeSecrets.ToList(), scopeSecrets);
                      result.ScopeClaimValues = scopeClaimValues;
                      result.ScopeSecretValues = scopeSecrets;
                      return new IdentityAdminResult<ScopeDetail>(result);
                  }
                  return new IdentityAdminResult<ScopeDetail>((ScopeDetail)null);
              }
          }
  
          public Task<IdentityAdminResult<QueryResult<ScopeSummary>>> QueryScopesAsync(string filter, int start, int count)
          {
              using (var db = new ScopeConfigurationDbContext(_entityFrameworkServiceOptions.ConnectionString, _entityFrameworkServiceOptions.Schema))
              {
                  var query =
                      from scope in db.Scopes
                      orderby scope.Name
                      select scope;
  
                  if (!String.IsNullOrWhiteSpace(filter))
                  {
                      query =
                          from scope in query
                          where scope.Name.Contains(filter)
                          orderby scope.Name
                          select scope;
                  }
  
                  int total = query.Count();
  
gt:                 var scopes = query.Skip(start).Take(count).ToArray();