mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Merged core into master
This commit is contained in:
commit
09630a6714
@ -1,9 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
end_of_line = crlf
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = true
|
||||
|
||||
[*.cshtml]
|
||||
indent_size = 4
|
154
.gitignore
vendored
154
.gitignore
vendored
@ -4,26 +4,34 @@
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
build/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Roslyn cache directories
|
||||
*.ide/
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
#NUNIT
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
@ -32,6 +40,11 @@ TestResult.xml
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
@ -64,14 +77,18 @@ _Chutzpah*
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
@ -84,7 +101,7 @@ _ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding addin-in
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
@ -96,6 +113,7 @@ _TeamCity*
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
@ -123,43 +141,63 @@ publish/
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
## TODO: Comment the next line if you want to checkin your
|
||||
## web deploy settings but do note that will include unencrypted
|
||||
## passwords
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages/*
|
||||
## TODO: If the tool you use requires repositories.config
|
||||
## uncomment the next line
|
||||
#!packages/repositories.config
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since
|
||||
# NuGet packages use it for MSBuild targets.
|
||||
# This line needs to be after the ignore of the build folder
|
||||
# (and the packages folder if the line above has been uncommented)
|
||||
!packages/build/
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Windows Azure Build Output
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
bower_components/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
@ -184,26 +222,50 @@ UpgradeLog*.htm
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
/Teknik/ConnectionStrings.config
|
||||
/Teknik/App_Data/Config.json
|
||||
/.vs/config/applicationhost.config
|
||||
/Teknik/TransformWebConfig/assist/Web.config
|
||||
/Teknik/Properties/PublishProfiles/IIS.pubxml
|
||||
/Teknik/App_Data/ConnectionStrings.config
|
||||
/Teknik/App_Data/Config.json.old
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
/Teknik/App_Data/MachineKey.config
|
||||
/.vs/Teknik/v15/sqlite3/storage.ide
|
||||
/.vs/Teknik/v15/sqlite3/storage.ide-wal
|
||||
/.vs/Teknik/v15/sqlite3/storage.ide-shm
|
||||
/.vs/Teknik/v15/sqlite3/db.lock
|
||||
/.vs/Teknik/v15/sqlite3
|
||||
/.vs/Teknik/v15/Server/sqlite3/storage.ide-wal
|
||||
/.vs/Teknik/v15/Server/sqlite3/storage.ide-shm
|
||||
/.vs/Teknik/v15/Server/sqlite3/storage.ide
|
||||
/.vs/Teknik/v15/Server/sqlite3/db.lock
|
||||
/.vs/Teknik/v15/Server/sqlite3
|
||||
/.vs/Teknik/v15
|
||||
/Teknik/App_Data/ConnectionStrings.config
|
||||
/Teknik/App_Data/Config.json
|
||||
/Teknik/App_Data/version.json
|
||||
|
||||
**/appsettings.*.json
|
||||
**/tempkey.rsa
|
||||
/ServiceWorker/Properties/launchSettings.json
|
||||
/IdentityServer/App_Data/Config.json
|
||||
/ServiceWorker/Output
|
||||
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class ApiConfig
|
||||
{
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class BlogConfig
|
||||
{
|
@ -1,10 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using Newtonsoft.Json;
|
||||
using Teknik.Utilities;
|
||||
using Teknik.Utilities.Cryptography;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
@ -12,47 +10,55 @@ namespace Teknik.Configuration
|
||||
public class Config
|
||||
{
|
||||
private const string _ConfigCacheKey = "ConfigCache";
|
||||
private const string _ConfigFileName = "Config.json";
|
||||
|
||||
private static Config _Config { get; set; }
|
||||
|
||||
private static string _FileHash { get; set; }
|
||||
|
||||
private ReaderWriterLockSlim _ConfigRWLock;
|
||||
private ReaderWriterLockSlim _ConfigFileRWLock;
|
||||
private JsonSerializerSettings _JsonSettings;
|
||||
|
||||
private bool _DevEnvironment;
|
||||
private bool _Migrate;
|
||||
private bool _UseCdn;
|
||||
private string _Title;
|
||||
private string _Description;
|
||||
private string _Author;
|
||||
private string _Host;
|
||||
private string _SupportEmail;
|
||||
private string _NoReplyEmail;
|
||||
private string _BitcoinAddress;
|
||||
private string _Salt1;
|
||||
private string _Salt2;
|
||||
private string _CdnHost;
|
||||
private string _IPBlacklistFile;
|
||||
private string _ReferrerBlacklistFile;
|
||||
private UserConfig _UserConfig;
|
||||
private ContactConfig _ContactConfig;
|
||||
private EmailConfig _EmailConfig;
|
||||
private GitConfig _GitConfig;
|
||||
private UploadConfig _UploadConfig;
|
||||
private PasteConfig _PasteConfig;
|
||||
private BlogConfig _BlogConfig;
|
||||
private ApiConfig _ApiConfig;
|
||||
private PodcastConfig _PodcastConfig;
|
||||
private StreamConfig _StreamConfig;
|
||||
private ShortenerConfig _ShortenerConfig;
|
||||
private VaultConfig _VaultConfig;
|
||||
private StatsConfig _StatsConfig;
|
||||
private LoggingConfig _LoggingConfig;
|
||||
private PiwikConfig _PiwikConfig;
|
||||
private IRCConfig _IRCConfig;
|
||||
private bool _DevEnvironment;
|
||||
private bool _Migrate;
|
||||
private bool _UseCdn;
|
||||
private string _DbConnection;
|
||||
private string _Title;
|
||||
private string _Description;
|
||||
private string _Author;
|
||||
private string _Host;
|
||||
private string _SupportEmail;
|
||||
private string _NoReplyEmail;
|
||||
private string _BitcoinAddress;
|
||||
private string _Salt1;
|
||||
private string _Salt2;
|
||||
private string _CdnHost;
|
||||
private string _IPBlacklistFile;
|
||||
private string _ReferrerBlacklistFile;
|
||||
private List<string> _PublicKeys;
|
||||
private UserConfig _UserConfig;
|
||||
private ContactConfig _ContactConfig;
|
||||
private EmailConfig _EmailConfig;
|
||||
private GitConfig _GitConfig;
|
||||
private UploadConfig _UploadConfig;
|
||||
private PasteConfig _PasteConfig;
|
||||
private BlogConfig _BlogConfig;
|
||||
private ApiConfig _ApiConfig;
|
||||
private PodcastConfig _PodcastConfig;
|
||||
private StreamConfig _StreamConfig;
|
||||
private ShortenerConfig _ShortenerConfig;
|
||||
private VaultConfig _VaultConfig;
|
||||
private StatsConfig _StatsConfig;
|
||||
private LoggingConfig _LoggingConfig;
|
||||
private PiwikConfig _PiwikConfig;
|
||||
private IRCConfig _IRCConfig;
|
||||
|
||||
public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } }
|
||||
public bool Migrate { get { return _Migrate; } set { _Migrate = value; } }
|
||||
public bool UseCdn { get { return _UseCdn; } set { _UseCdn = value; } }
|
||||
|
||||
public string DbConnection { get { return _DbConnection; } set { _DbConnection = value; } }
|
||||
|
||||
// Site Information
|
||||
public string Title { get { return _Title; } set { _Title = value; } }
|
||||
public string Description { get { return _Description; } set { _Description = value; } }
|
||||
@ -67,56 +73,60 @@ namespace Teknik.Configuration
|
||||
public string IPBlacklistFile { get { return _IPBlacklistFile;} set { _IPBlacklistFile = value; }}
|
||||
public string ReferrerBlacklistFile { get { return _ReferrerBlacklistFile;} set { _ReferrerBlacklistFile = value; }}
|
||||
|
||||
public List<string> PublicKeys { get { return _PublicKeys; } set { _PublicKeys = value; } }
|
||||
|
||||
// User Configuration
|
||||
public UserConfig UserConfig { get { return _UserConfig; } set { _UserConfig = value; } }
|
||||
public UserConfig UserConfig { get { return _UserConfig; } set { _UserConfig = value; } }
|
||||
|
||||
// Contact Configuration
|
||||
public ContactConfig ContactConfig { get { return _ContactConfig; } set { _ContactConfig = value; } }
|
||||
public ContactConfig ContactConfig { get { return _ContactConfig; } set { _ContactConfig = value; } }
|
||||
|
||||
// Mail Server Configuration
|
||||
public EmailConfig EmailConfig { get { return _EmailConfig; } set { _EmailConfig = value; } }
|
||||
public EmailConfig EmailConfig { get { return _EmailConfig; } set { _EmailConfig = value; } }
|
||||
|
||||
// Git Service Configuration
|
||||
public GitConfig GitConfig { get { return _GitConfig; } set { _GitConfig = value; } }
|
||||
public GitConfig GitConfig { get { return _GitConfig; } set { _GitConfig = value; } }
|
||||
|
||||
// Blog Configuration
|
||||
public BlogConfig BlogConfig { get { return _BlogConfig; } set { _BlogConfig = value; } }
|
||||
public BlogConfig BlogConfig { get { return _BlogConfig; } set { _BlogConfig = value; } }
|
||||
|
||||
// Upload Configuration
|
||||
public UploadConfig UploadConfig { get { return _UploadConfig; } set { _UploadConfig = value; } }
|
||||
public UploadConfig UploadConfig { get { return _UploadConfig; } set { _UploadConfig = value; } }
|
||||
|
||||
// Paste Configuration
|
||||
public PasteConfig PasteConfig { get { return _PasteConfig; } set { _PasteConfig = value; } }
|
||||
public PasteConfig PasteConfig { get { return _PasteConfig; } set { _PasteConfig = value; } }
|
||||
|
||||
// API Configuration
|
||||
public ApiConfig ApiConfig { get { return _ApiConfig; } set { _ApiConfig = value; } }
|
||||
public ApiConfig ApiConfig { get { return _ApiConfig; } set { _ApiConfig = value; } }
|
||||
|
||||
// Podcast Configuration
|
||||
public PodcastConfig PodcastConfig { get { return _PodcastConfig; } set { _PodcastConfig = value; } }
|
||||
public PodcastConfig PodcastConfig { get { return _PodcastConfig; } set { _PodcastConfig = value; } }
|
||||
|
||||
// Stream Configuration
|
||||
public StreamConfig StreamConfig { get { return _StreamConfig; } set { _StreamConfig = value; } }
|
||||
public StreamConfig StreamConfig { get { return _StreamConfig; } set { _StreamConfig = value; } }
|
||||
|
||||
// Shortener Configuration
|
||||
public ShortenerConfig ShortenerConfig { get { return _ShortenerConfig; } set { _ShortenerConfig = value; } }
|
||||
public ShortenerConfig ShortenerConfig { get { return _ShortenerConfig; } set { _ShortenerConfig = value; } }
|
||||
|
||||
// Vault Configuration
|
||||
public VaultConfig VaultConfig { get { return _VaultConfig; } set { _VaultConfig = value; } }
|
||||
public VaultConfig VaultConfig { get { return _VaultConfig; } set { _VaultConfig = value; } }
|
||||
|
||||
// Status Configuration
|
||||
public StatsConfig StatsConfig { get { return _StatsConfig; } set { _StatsConfig = value; } }
|
||||
public StatsConfig StatsConfig { get { return _StatsConfig; } set { _StatsConfig = value; } }
|
||||
|
||||
// Logging Configuration
|
||||
public LoggingConfig LoggingConfig { get { return _LoggingConfig; } set { _LoggingConfig = value; } }
|
||||
public LoggingConfig LoggingConfig { get { return _LoggingConfig; } set { _LoggingConfig = value; } }
|
||||
|
||||
// Piwik Configuration
|
||||
public PiwikConfig PiwikConfig { get { return _PiwikConfig; } set { _PiwikConfig = value; } }
|
||||
public PiwikConfig PiwikConfig { get { return _PiwikConfig; } set { _PiwikConfig = value; } }
|
||||
|
||||
// Piwik Configuration
|
||||
public IRCConfig IRCConfig { get { return _IRCConfig; } set { _IRCConfig = value; } }
|
||||
public IRCConfig IRCConfig { get { return _IRCConfig; } set { _IRCConfig = value; } }
|
||||
|
||||
public Config()
|
||||
{
|
||||
_ConfigRWLock = new ReaderWriterLockSlim();
|
||||
_ConfigFileRWLock = new ReaderWriterLockSlim();
|
||||
_JsonSettings = new JsonSerializerSettings();
|
||||
_JsonSettings.Formatting = Formatting.Indented;
|
||||
|
||||
@ -140,6 +150,7 @@ namespace Teknik.Configuration
|
||||
CdnHost = string.Empty;
|
||||
IPBlacklistFile = string.Empty;
|
||||
ReferrerBlacklistFile = string.Empty;
|
||||
PublicKeys = new List<string>();
|
||||
UserConfig = new UserConfig();
|
||||
EmailConfig = new EmailConfig();
|
||||
ContactConfig = new ContactConfig();
|
||||
@ -168,39 +179,26 @@ namespace Teknik.Configuration
|
||||
return JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||
}
|
||||
|
||||
public static Config Load()
|
||||
{
|
||||
HttpContext context = HttpContext.Current;
|
||||
if (context != null)
|
||||
_Config = (Config)context.Cache[_ConfigCacheKey];
|
||||
if (_Config == null)
|
||||
{
|
||||
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
|
||||
_Config = Load(path);
|
||||
context?.Cache.Insert(_ConfigCacheKey, _Config, new CacheDependency(path));
|
||||
}
|
||||
return _Config;
|
||||
}
|
||||
|
||||
public static Config Load(string path)
|
||||
{
|
||||
Config config = new Config();
|
||||
if (!File.Exists(Path.Combine(path, "Config.json")))
|
||||
{
|
||||
Save(Path.Combine(path, "Config.json"), config);
|
||||
}
|
||||
else
|
||||
{
|
||||
string configContents = File.ReadAllText(Path.Combine(path, "Config.json"));
|
||||
config = Deserialize(configContents);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
string newHash = string.Empty;
|
||||
string fullPath = Path.Combine(path, _ConfigFileName);
|
||||
|
||||
public static void Save(Config config)
|
||||
{
|
||||
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
|
||||
Save(Path.Combine(path, "Config.json"), config);
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
Config config = new Config();
|
||||
Save(fullPath, config);
|
||||
}
|
||||
|
||||
newHash = MD5.FileHash(fullPath);
|
||||
|
||||
if (_Config == null || _FileHash == null || newHash != _FileHash)
|
||||
{
|
||||
string configContents = File.ReadAllText(fullPath);
|
||||
_Config = Deserialize(configContents);
|
||||
_FileHash = newHash;
|
||||
}
|
||||
return _Config;
|
||||
}
|
||||
|
||||
public static void Save(string path, Config config)
|
19
Configuration/Configuration.csproj
Normal file
19
Configuration/Configuration.csproj
Normal file
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RootNamespace>Teknik.Configuration</RootNamespace>
|
||||
<AssemblyName>Teknik.Configuration</AssemblyName>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||
<Configurations>Debug;Release;Test</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Utilities\Utilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,11 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class ContactConfig
|
||||
{
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class EmailAccount
|
||||
{
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class EmailConfig
|
||||
{
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class GitConfig
|
||||
{
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class IRCConfig
|
||||
{
|
32
Configuration/IdentityServerConfig.cs
Normal file
32
Configuration/IdentityServerConfig.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class IdentityServerConfig
|
||||
{
|
||||
public string Authority { get; set; }
|
||||
|
||||
public string ClientId { get; set; }
|
||||
public string ClientSecret { get; set; }
|
||||
public List<string> RedirectUris { get; set; }
|
||||
public List<string> PostLogoutRedirectUris { get; set; }
|
||||
public List<string> AllowedCorsOrigins { get; set; }
|
||||
|
||||
public string APIName { get; set; }
|
||||
public string APISecret { get; set; }
|
||||
|
||||
public IdentityServerConfig()
|
||||
{
|
||||
Authority = "https://localhost:5002";
|
||||
ClientId = "mvc.client";
|
||||
ClientSecret = "mysecret";
|
||||
RedirectUris = new List<string>();
|
||||
PostLogoutRedirectUris = new List<string>();
|
||||
AllowedCorsOrigins = new List<string>();
|
||||
APIName = "api";
|
||||
APISecret = "secret";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class LoggingConfig
|
||||
{
|
33
Configuration/PasteConfig.cs
Normal file
33
Configuration/PasteConfig.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class PasteConfig
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public int UrlLength { get; set; }
|
||||
public int DeleteKeyLength { get; set; }
|
||||
public string SyntaxVisualStyle { get; set; }
|
||||
// Location of the upload directory
|
||||
public string PasteDirectory { get; set; }
|
||||
// File Extension for saved files
|
||||
public string FileExtension { get; set; }
|
||||
public int KeySize { get; set; }
|
||||
public int BlockSize { get; set; }
|
||||
// The size of the chunk that the file will be encrypted/decrypted in (bytes)
|
||||
public int ChunkSize { get; set; }
|
||||
|
||||
public PasteConfig()
|
||||
{
|
||||
Enabled = true;
|
||||
UrlLength = 5;
|
||||
DeleteKeyLength = 24;
|
||||
KeySize = 256;
|
||||
BlockSize = 128;
|
||||
ChunkSize = 1040;
|
||||
PasteDirectory = Directory.GetCurrentDirectory();
|
||||
FileExtension = "enc";
|
||||
SyntaxVisualStyle = "vs";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class PiwikConfig
|
||||
{
|
@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.IO;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
{
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class ShortenerConfig
|
||||
{
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class StatsConfig
|
||||
{
|
@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
{
|
@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
{
|
@ -1,10 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class UserConfig
|
||||
@ -20,6 +13,7 @@ namespace Teknik.Configuration
|
||||
public decimal PremiumAccountPrice { get; set; }
|
||||
public string PaymentType { get; set; }
|
||||
public bool InviteCodeRequired { get; set; }
|
||||
public IdentityServerConfig IdentityServerConfig { get; set; }
|
||||
|
||||
public UserConfig()
|
||||
{
|
||||
@ -34,6 +28,7 @@ namespace Teknik.Configuration
|
||||
PremiumAccountPrice = 0;
|
||||
PaymentType = "Donation";
|
||||
InviteCodeRequired = false;
|
||||
IdentityServerConfig = new IdentityServerConfig();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Configuration
|
||||
namespace Teknik.Configuration
|
||||
{
|
||||
public class VaultConfig
|
||||
{
|
15
GitService/GitService.csproj
Normal file
15
GitService/GitService.csproj
Normal file
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>Teknik.GitService</AssemblyName>
|
||||
<RootNamespace>Teknik.GitService</RootNamespace>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.Data" Version="8.0.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
148
GitService/GiteaService.cs
Normal file
148
GitService/GiteaService.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Teknik.GitService
|
||||
{
|
||||
public class GiteaService : IGitService
|
||||
{
|
||||
private readonly int _sourceId;
|
||||
private readonly string _host;
|
||||
private readonly string _accessToken;
|
||||
|
||||
private readonly string _server;
|
||||
private readonly string _database;
|
||||
private readonly string _username;
|
||||
private readonly string _password;
|
||||
private readonly int _port;
|
||||
|
||||
public GiteaService(int sourceId, string host, string accessToken, string server, string database, string username, string password, int port)
|
||||
{
|
||||
_sourceId = sourceId;
|
||||
_host = host;
|
||||
_accessToken = accessToken;
|
||||
|
||||
_server = server;
|
||||
_database = database;
|
||||
_username = username;
|
||||
_password = password;
|
||||
_port = port;
|
||||
}
|
||||
|
||||
public bool AccountExists(string username)
|
||||
{
|
||||
Uri baseUri = new Uri(_host);
|
||||
Uri finalUri = new Uri(baseUri, "api/v1/users/" + username + "?token=" + _accessToken);
|
||||
WebRequest request = WebRequest.Create(finalUri);
|
||||
request.Method = "GET";
|
||||
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CreateAccount(string username, string email, string password)
|
||||
{
|
||||
// Add gogs user
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
var obj = new { source_id = _sourceId, username = username, email = email, login_name = email, password = password };
|
||||
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
|
||||
client.Headers[HttpRequestHeader.ContentType] = "application/json";
|
||||
Uri baseUri = new Uri(_host);
|
||||
Uri finalUri = new Uri(baseUri, "api/v1/admin/users?token=" + _accessToken);
|
||||
string result = client.UploadString(finalUri, "POST", json);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAccount(string username)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri baseUri = new Uri(_host);
|
||||
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + username + "?token=" + _accessToken);
|
||||
WebRequest request = WebRequest.Create(finalUri);
|
||||
request.Method = "DELETE";
|
||||
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
if (response.StatusCode != HttpStatusCode.NotFound && response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.NoContent)
|
||||
{
|
||||
throw new Exception("Response Code: " + response.StatusCode);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// This error signifies the user doesn't exist, so we can continue deleting
|
||||
if (ex.Message != "The remote server returned an error: (404) Not Found.")
|
||||
{
|
||||
throw new Exception("Unable to delete git account. Exception: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EditPassword(string username, string email, string password)
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
var obj = new { source_id = _sourceId, email = email, login_name = email, password = password };
|
||||
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
|
||||
client.Headers[HttpRequestHeader.ContentType] = "application/json";
|
||||
Uri baseUri = new Uri(_host);
|
||||
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + username + "?token=" + _accessToken);
|
||||
string result = client.UploadString(finalUri, "PATCH", json);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableAccount(string username, string email)
|
||||
{
|
||||
ChangeAccountStatus(username, email, true);
|
||||
}
|
||||
|
||||
public void DisableAccount(string username, string email)
|
||||
{
|
||||
ChangeAccountStatus(username, email, false);
|
||||
}
|
||||
|
||||
public void ChangeAccountStatus(string username, string email, bool active)
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
var obj = new { active = active, email = email };
|
||||
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
|
||||
client.Headers[HttpRequestHeader.ContentType] = "application/json";
|
||||
Uri baseUri = new Uri(_host);
|
||||
Uri finalUri = new Uri(baseUri, "api/v1/admin/users/" + username + "?token=" + _accessToken);
|
||||
string result = client.UploadString(finalUri, "PATCH", json);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime LastActive(string email)
|
||||
{
|
||||
// We need to check the actual git database
|
||||
MysqlDatabase mySQL = new MysqlDatabase(_server, _database, _username, _password, _port);
|
||||
string sql = @"SELECT
|
||||
CASE
|
||||
WHEN MAX(gogs.action.created) >= MAX(gogs.user.updated) THEN MAX(gogs.action.created)
|
||||
WHEN MAX(gogs.user.updated) >= MAX(gogs.action.created) THEN MAX(gogs.user.updated)
|
||||
ELSE MAX(gogs.user.updated)
|
||||
END AS LastUpdate
|
||||
FROM gogs.user
|
||||
LEFT JOIN gogs.action ON gogs.user.id = gogs.action.act_user_id
|
||||
WHERE gogs.user.login_name = {0}";
|
||||
var results = mySQL.Query(sql, new object[] { email });
|
||||
|
||||
DateTime lastActive = new DateTime(1, 0, 0);
|
||||
if (results != null && results.Any())
|
||||
{
|
||||
var result = results.First();
|
||||
DateTime.TryParse(result["LastUpdate"].ToString(), out lastActive);
|
||||
}
|
||||
return lastActive;
|
||||
}
|
||||
}
|
||||
}
|
23
GitService/IGitService.cs
Normal file
23
GitService/IGitService.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Teknik.GitService
|
||||
{
|
||||
public interface IGitService
|
||||
{
|
||||
bool AccountExists(string username);
|
||||
|
||||
DateTime LastActive(string username);
|
||||
|
||||
void CreateAccount(string username, string email, string password);
|
||||
|
||||
void EditPassword(string username, string email, string password);
|
||||
|
||||
void EnableAccount(string username, string email);
|
||||
|
||||
void DisableAccount(string username, string email);
|
||||
|
||||
void DeleteAccount(string username);
|
||||
}
|
||||
}
|
173
GitService/MysqlDatabase.cs
Normal file
173
GitService/MysqlDatabase.cs
Normal file
@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Teknik.GitService
|
||||
{
|
||||
public class MysqlDatabase
|
||||
{
|
||||
public event EventHandler<string> MysqlErrorEvent;
|
||||
|
||||
private bool Connected { get; set; }
|
||||
private MySqlConnection Connection { get; set; }
|
||||
private ReaderWriterLockSlim DatabaseLock { get; set; }
|
||||
|
||||
public MysqlDatabase(string server, string database, string username, string password, int port)
|
||||
{
|
||||
Connected = false;
|
||||
Connection = null;
|
||||
DatabaseLock = new ReaderWriterLockSlim();
|
||||
Connect(server, database, username, password, port);
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> Query(string query, params object[] args)
|
||||
{
|
||||
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
|
||||
if (Connected)
|
||||
{
|
||||
DatabaseLock.EnterWriteLock();
|
||||
MySqlCommand cmd = PrepareQuery(query, args);
|
||||
try
|
||||
{
|
||||
MySqlDataReader reader = cmd.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
Dictionary<string, object> row = new Dictionary<string, object>();
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
row.Add(reader.GetName(i), reader.GetValue(i));
|
||||
}
|
||||
rows.Add(row);
|
||||
}
|
||||
reader.Close();
|
||||
}
|
||||
catch (MySqlException exception)
|
||||
{
|
||||
if (MysqlErrorEvent != null)
|
||||
{
|
||||
MysqlErrorEvent(this, exception.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (MysqlErrorEvent != null)
|
||||
{
|
||||
MysqlErrorEvent(this, exception.Message);
|
||||
}
|
||||
}
|
||||
DatabaseLock.ExitWriteLock();
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
public object ScalarQuery(string query, params object[] args)
|
||||
{
|
||||
if (Connected)
|
||||
{
|
||||
DatabaseLock.EnterWriteLock();
|
||||
MySqlCommand cmd = PrepareQuery(query, args);
|
||||
object result = null;
|
||||
try
|
||||
{
|
||||
|
||||
result = cmd.ExecuteScalar();
|
||||
}
|
||||
catch (MySqlException exception)
|
||||
{
|
||||
if (MysqlErrorEvent != null)
|
||||
{
|
||||
MysqlErrorEvent(this, exception.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (MysqlErrorEvent != null)
|
||||
{
|
||||
MysqlErrorEvent(this, exception.Message);
|
||||
}
|
||||
}
|
||||
DatabaseLock.ExitWriteLock();
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Execute(string query, params object[] args)
|
||||
{
|
||||
if (Connected)
|
||||
{
|
||||
DatabaseLock.EnterWriteLock();
|
||||
MySqlCommand cmd = PrepareQuery(query, args);
|
||||
try
|
||||
{
|
||||
int result = cmd.ExecuteNonQuery();
|
||||
}
|
||||
catch (MySqlException exception)
|
||||
{
|
||||
if (MysqlErrorEvent != null)
|
||||
{
|
||||
MysqlErrorEvent(this, exception.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (MysqlErrorEvent != null)
|
||||
{
|
||||
MysqlErrorEvent(this, exception.Message);
|
||||
}
|
||||
}
|
||||
DatabaseLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
private void Connect(string server, string database, string username, string password, int port)
|
||||
{
|
||||
if (Connection == null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(server) && !string.IsNullOrEmpty(database) && !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
|
||||
{
|
||||
string strCon = string.Format("Server={0}; database={1}; user={2}; password={3}; port={4}; charset=utf8; Allow Zero Datetime=true;", server, database, username, password, port);
|
||||
Connection = new MySqlConnection(strCon);
|
||||
try
|
||||
{
|
||||
Connection.Open();
|
||||
Connected = true;
|
||||
}
|
||||
catch (MySqlException)
|
||||
{
|
||||
Connected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
{
|
||||
if (Connection != null && Connected)
|
||||
{
|
||||
Connected = false;
|
||||
Connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private MySqlCommand PrepareQuery(string query, object[] args)
|
||||
{
|
||||
if (Connected)
|
||||
{
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
cmd.Connection = Connection;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
string param = "{" + i + "}";
|
||||
string paramName = "@DBVar_" + i;
|
||||
query = query.Replace(param, paramName);
|
||||
cmd.Parameters.AddWithValue(paramName, args[i]);
|
||||
}
|
||||
cmd.CommandText = query;
|
||||
return cmd;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
assembly-versioning-scheme: MajorMinorPatch
|
||||
next-version: 2.0.6
|
52
IdentityServer.sln
Normal file
52
IdentityServer.sln
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.102
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer", "IdentityServer\IdentityServer.csproj", "{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Configuration", "Configuration\Configuration.csproj", "{056A17AC-98EB-49E1-8316-D632D9D1E7E3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Logging", "Logging\Logging.csproj", "{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utilities", "Utilities\Utilities.csproj", "{EF512B49-C638-4FEE-BA89-913BE9F90951}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Test|Any CPU = Test|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}.Test|Any CPU.ActiveCfg = Test|Any CPU
|
||||
{EB46E1EB-FDC2-4168-A0AD-8B284D44B13E}.Test|Any CPU.Build.0 = Test|Any CPU
|
||||
{056A17AC-98EB-49E1-8316-D632D9D1E7E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{056A17AC-98EB-49E1-8316-D632D9D1E7E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{056A17AC-98EB-49E1-8316-D632D9D1E7E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{056A17AC-98EB-49E1-8316-D632D9D1E7E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{056A17AC-98EB-49E1-8316-D632D9D1E7E3}.Test|Any CPU.ActiveCfg = Test|Any CPU
|
||||
{056A17AC-98EB-49E1-8316-D632D9D1E7E3}.Test|Any CPU.Build.0 = Test|Any CPU
|
||||
{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}.Test|Any CPU.ActiveCfg = Test|Any CPU
|
||||
{F4C3F912-73EF-433B-A06E-1F2C5F2F4A65}.Test|Any CPU.Build.0 = Test|Any CPU
|
||||
{EF512B49-C638-4FEE-BA89-913BE9F90951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF512B49-C638-4FEE-BA89-913BE9F90951}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF512B49-C638-4FEE-BA89-913BE9F90951}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF512B49-C638-4FEE-BA89-913BE9F90951}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF512B49-C638-4FEE-BA89-913BE9F90951}.Test|Any CPU.ActiveCfg = Test|Any CPU
|
||||
{EF512B49-C638-4FEE-BA89-913BE9F90951}.Test|Any CPU.Build.0 = Test|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {01391BA1-ABE9-489E-827C-1F0165F185F0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
11
IdentityServer/ApplicationDbContext.cs
Normal file
11
IdentityServer/ApplicationDbContext.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Teknik.IdentityServer.Models;
|
||||
|
||||
namespace Teknik.IdentityServer
|
||||
{
|
||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
||||
}
|
||||
}
|
143
IdentityServer/Configuration.cs
Normal file
143
IdentityServer/Configuration.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using IdentityModel;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Test;
|
||||
using Teknik.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer.Configuration
|
||||
{
|
||||
internal class Clients
|
||||
{
|
||||
public static IEnumerable<Client> Get(Config config)
|
||||
{
|
||||
return new List<Client> {
|
||||
new Client
|
||||
{
|
||||
ClientId = config.UserConfig.IdentityServerConfig.ClientId,
|
||||
ClientName = "Teknik Web Services",
|
||||
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
|
||||
|
||||
ClientSecrets =
|
||||
{
|
||||
new Secret(config.UserConfig.IdentityServerConfig.ClientSecret.Sha256())
|
||||
},
|
||||
|
||||
RequireConsent = false,
|
||||
|
||||
AllowedScopes =
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
"role",
|
||||
"account-info",
|
||||
"security-info",
|
||||
"teknik-api.read",
|
||||
"teknik-api.write",
|
||||
"auth-api"
|
||||
},
|
||||
AllowOfflineAccess = true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal class Resources
|
||||
{
|
||||
public static IEnumerable<IdentityResource> GetIdentityResources()
|
||||
{
|
||||
return new List<IdentityResource> {
|
||||
new IdentityResources.OpenId(),
|
||||
new IdentityResource
|
||||
{
|
||||
Name = "account-info",
|
||||
DisplayName = "Account Info",
|
||||
UserClaims = new List<string>
|
||||
{
|
||||
"username",
|
||||
"email",
|
||||
"creation-date",
|
||||
"last-seen",
|
||||
"account-type",
|
||||
"account-status"
|
||||
}
|
||||
},
|
||||
new IdentityResource
|
||||
{
|
||||
Name = "security-info",
|
||||
DisplayName = "Security Info",
|
||||
UserClaims = new List<string>
|
||||
{
|
||||
"recovery-email",
|
||||
"recovery-verified",
|
||||
"pgp-public-key"
|
||||
}
|
||||
},
|
||||
new IdentityResource {
|
||||
Name = "role",
|
||||
DisplayName = "Role",
|
||||
UserClaims = new List<string> {"role"}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<ApiResource> GetApiResources(Config config)
|
||||
{
|
||||
return new List<ApiResource> {
|
||||
new ApiResource {
|
||||
Name = config.UserConfig.IdentityServerConfig.APIName,
|
||||
DisplayName = "Teknik API",
|
||||
Description = "Teknik API Access for end users",
|
||||
UserClaims = new List<string> {"role", "username"},
|
||||
ApiSecrets = new List<Secret> {new Secret(config.UserConfig.IdentityServerConfig.APISecret.Sha256()) },
|
||||
Scopes = new List<Scope> {
|
||||
new Scope("teknik-api.read", "Teknik API Read Access"),
|
||||
new Scope("teknik-api.write", "Teknik API Write Access")
|
||||
}
|
||||
},
|
||||
new ApiResource {
|
||||
Name = "auth-api",
|
||||
DisplayName = "Auth Server API",
|
||||
Description = "Auth Server API Access for managing the Auth Server",
|
||||
Scopes = new List<Scope> {
|
||||
new Scope()
|
||||
{
|
||||
Name = "auth-api",
|
||||
ShowInDiscoveryDocument = false,
|
||||
Required = true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal class Policies
|
||||
{
|
||||
public static IEnumerable<Policy> Get()
|
||||
{
|
||||
return new List<Policy>
|
||||
{
|
||||
new Policy
|
||||
{
|
||||
Name = "Internal",
|
||||
Scopes = { "auth-api" }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal class Policy
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ICollection<string> Scopes { get; set; }
|
||||
|
||||
public Policy()
|
||||
{
|
||||
Name = string.Empty;
|
||||
Scopes = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
140
IdentityServer/Content/common.css
Normal file
140
IdentityServer/Content/common.css
Normal file
@ -0,0 +1,140 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
/* The html and body elements cannot have any padding or margin. */
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
/* Wrapper for page content to push down footer */
|
||||
#wrap {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
/* Negative indent footer by its height */
|
||||
margin: 0 auto -27px;
|
||||
/* Pad bottom by footer height */
|
||||
padding: 0 0 27px;
|
||||
}
|
||||
|
||||
/* Set the fixed height of the footer here */
|
||||
#footer {
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
background-color: #333;
|
||||
color: #f5f5f5;
|
||||
border: none;
|
||||
min-height: 12px;
|
||||
}
|
||||
|
||||
#footer .btn, #footer .input-group-addon, #footer .form-control {
|
||||
background-color: #444;
|
||||
color: #dedede;
|
||||
border-color: #4a4a4a;
|
||||
}
|
||||
|
||||
.navbar-header {
|
||||
position: relative;
|
||||
top: -4px;
|
||||
}
|
||||
.navbar-brand > .icon-banner {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
display: inline;
|
||||
}
|
||||
label {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
.icon {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
.abc-checkbox label {
|
||||
display: inline-block !important;
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
.logged-out iframe {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.page-consent .client-logo {
|
||||
float: left;
|
||||
}
|
||||
.page-consent .client-logo img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.page-consent .consent-buttons {
|
||||
margin-top: 25px;
|
||||
}
|
||||
.page-consent .consent-form .consent-scopecheck {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.page-consent .consent-form .consent-description {
|
||||
margin-left: 25px;
|
||||
}
|
||||
.page-consent .consent-form .consent-description label {
|
||||
font-weight: normal;
|
||||
}
|
||||
.page-consent .consent-form .consent-remember {
|
||||
padding-left: 16px;
|
||||
}
|
||||
.grants .page-header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.grants .grant {
|
||||
margin-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid lightgray;
|
||||
}
|
||||
.grants .grant img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.grants .grant .clientname {
|
||||
font-size: 140%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.grants .grant .granttype {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.grants .grant .created {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.grants .grant .expires {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.grants .grant li {
|
||||
list-style-type: none;
|
||||
display: inline;
|
||||
}
|
||||
.grants .grant li:after {
|
||||
content: ', ';
|
||||
}
|
||||
.grants .grant li:last-child:after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
.validation-summary-errors ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.validation-summary-errors ul li {
|
||||
margin: 5px 0;
|
||||
}
|
324
IdentityServer/Controllers/AccountController.cs
Normal file
324
IdentityServer/Controllers/AccountController.cs
Normal file
@ -0,0 +1,324 @@
|
||||
using IdentityModel;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using IdentityServer4.Test;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using IdentityServer4.Events;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Teknik.IdentityServer.Security;
|
||||
using Teknik.IdentityServer.Services;
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
using Teknik.IdentityServer.Options;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Teknik.Logging;
|
||||
using Teknik.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
public class AccountController : DefaultController
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IEventService _events;
|
||||
private readonly AccountService _account;
|
||||
|
||||
public AccountController(
|
||||
ILogger<Logger> logger,
|
||||
Config config,
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clientStore,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IAuthenticationSchemeProvider schemeProvider,
|
||||
IEventService events,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager) : base(logger, config)
|
||||
{
|
||||
// if the TestUserStore is not in DI, then we'll just use the global users collection
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_interaction = interaction;
|
||||
_events = events;
|
||||
_account = new AccountService(interaction, httpContextAccessor, schemeProvider, clientStore);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show login page
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Login(string returnUrl)
|
||||
{
|
||||
ViewBag.Title = $"Sign in";
|
||||
// build a model so we know what to show on the login page
|
||||
var vm = await _account.BuildLoginViewModelAsync(returnUrl);
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle postback from username/password login
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string button, string returnUrl = null)
|
||||
{
|
||||
if (button != "login")
|
||||
{
|
||||
// the user clicked the "cancel" button
|
||||
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||
if (context != null)
|
||||
{
|
||||
// if the user cancels, send a result back into IdentityServer as if they
|
||||
// denied the consent (even if this client does not require consent).
|
||||
// this will send back an access denied OIDC error response to the client.
|
||||
await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);
|
||||
|
||||
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
// since we don't have a valid context, then we just go back to the home page
|
||||
return Redirect("~/");
|
||||
}
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// Check to see if the user is banned
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
if (foundUser.AccountStatus == Utilities.AccountStatus.Banned)
|
||||
{
|
||||
// Redirect to banned page
|
||||
return RedirectToAction(nameof(Banned));
|
||||
}
|
||||
|
||||
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, false);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// make sure the returnUrl is still valid, and if so redirect back to authorize endpoint or a local page
|
||||
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
||||
return Redirect("~/");
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
{
|
||||
// Redirect to 2FA page
|
||||
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
// Redirect to locked out page
|
||||
return RedirectToAction(nameof(Lockout));
|
||||
}
|
||||
}
|
||||
|
||||
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
|
||||
|
||||
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
|
||||
}
|
||||
|
||||
// something went wrong, show form with error
|
||||
var vm = await _account.BuildLoginViewModelAsync(model);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
|
||||
{
|
||||
ViewBag.Title = "Two-Factor Authentication";
|
||||
// Ensure the user has gone through the username & password screen first
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||
}
|
||||
|
||||
var model = new LoginWith2faViewModel { RememberMe = rememberMe };
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
|
||||
|
||||
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
else if (result.IsLockedOut)
|
||||
{
|
||||
return RedirectToAction(nameof(Lockout));
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
|
||||
return View();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
|
||||
{
|
||||
ViewBag.Title = "Two-Factor Recovery Code";
|
||||
// Ensure the user has gone through the username & password screen first
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||
}
|
||||
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||
}
|
||||
|
||||
var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty);
|
||||
|
||||
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
return RedirectToAction(nameof(Lockout));
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
|
||||
return View();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Lockout()
|
||||
{
|
||||
ViewBag.Title = "Locked Out";
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Banned()
|
||||
{
|
||||
ViewBag.Title = "Banned";
|
||||
return View();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show logout page
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Logout(string logoutId)
|
||||
{
|
||||
ViewBag.Title = "Logout";
|
||||
// build a model so the logout page knows what to display
|
||||
var vm = await _account.BuildLogoutViewModelAsync(logoutId);
|
||||
|
||||
if (vm.ShowLogoutPrompt == false)
|
||||
{
|
||||
// if the request for logout was properly authenticated from IdentityServer, then
|
||||
// we don't need to show the prompt and can just log the user out directly.
|
||||
return await Logout(vm);
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle logout page postback
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Logout(LogoutInputModel model)
|
||||
{
|
||||
// get context information (client name, post logout redirect URI and iframe for federated signout)
|
||||
var vm = await _account.BuildLoggedOutViewModelAsync(model.LogoutId);
|
||||
|
||||
if (User?.Identity.IsAuthenticated == true)
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
|
||||
// raise the logout event
|
||||
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
|
||||
}
|
||||
|
||||
return View("LoggedOut", vm);
|
||||
}
|
||||
|
||||
[HttpOptions]
|
||||
public async Task Logout()
|
||||
{
|
||||
if (User?.Identity.IsAuthenticated == true)
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
|
||||
// raise the logout event
|
||||
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
|
||||
}
|
||||
}
|
||||
|
||||
private IActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction(nameof(HomeController.Index), "Home");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
IdentityServer/Controllers/ConsentController.cs
Normal file
77
IdentityServer/Controllers/ConsentController.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using Teknik.IdentityServer.Security;
|
||||
using Teknik.IdentityServer.Services;
|
||||
using Teknik.Logging;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// This controller processes the consent UI
|
||||
/// </summary>
|
||||
public class ConsentController : DefaultController
|
||||
{
|
||||
private readonly ConsentService _consent;
|
||||
|
||||
public ConsentController(
|
||||
ILogger<Logger> logger,
|
||||
Config config,
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clientStore,
|
||||
IResourceStore resourceStore) : base(logger, config)
|
||||
{
|
||||
_consent = new ConsentService(interaction, clientStore, resourceStore, logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the consent screen
|
||||
/// </summary>
|
||||
/// <param name="returnUrl"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index(string returnUrl)
|
||||
{
|
||||
ViewBag.Title = "Application Consent";
|
||||
var vm = await _consent.BuildViewModelAsync(returnUrl);
|
||||
if (vm != null)
|
||||
{
|
||||
return View("Index", vm);
|
||||
}
|
||||
|
||||
throw new ApplicationException($"Unable to load consent view model.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the consent screen postback
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Index(ConsentInputModel model)
|
||||
{
|
||||
var result = await _consent.ProcessConsent(model);
|
||||
|
||||
if (result.IsRedirect)
|
||||
{
|
||||
return Redirect(result.RedirectUri);
|
||||
}
|
||||
|
||||
if (result.HasValidationError)
|
||||
{
|
||||
ModelState.AddModelError("", result.ValidationError);
|
||||
}
|
||||
|
||||
if (result.ShowView)
|
||||
{
|
||||
return View("Index", result.ViewModel);
|
||||
}
|
||||
|
||||
throw new ApplicationException($"Unable to load consent view model.");
|
||||
}
|
||||
}
|
||||
}
|
64
IdentityServer/Controllers/DefaultController.cs
Normal file
64
IdentityServer/Controllers/DefaultController.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.Logging;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
public class DefaultController : Controller
|
||||
{
|
||||
protected readonly ILogger<Logger> _logger;
|
||||
protected readonly Config _config;
|
||||
|
||||
public DefaultController(ILogger<Logger> logger, Config config)
|
||||
{
|
||||
_logger = logger;
|
||||
_config = config;
|
||||
|
||||
ViewBag.Title = string.Empty;
|
||||
ViewBag.Description = "Teknik Authentication Service";
|
||||
}
|
||||
|
||||
// Get the Favicon
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
[ResponseCache(Duration = 31536000, Location = ResponseCacheLocation.Any)]
|
||||
public IActionResult Favicon([FromServices] IHostingEnvironment env)
|
||||
{
|
||||
string imageFile = FileHelper.MapPath(env, Constants.FAVICON_PATH);
|
||||
FileStream fs = new FileStream(imageFile, FileMode.Open, FileAccess.Read);
|
||||
return File(fs, "image/x-icon");
|
||||
}
|
||||
|
||||
// Get the Robots.txt
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Robots([FromServices] IHostingEnvironment env)
|
||||
{
|
||||
//string file = FileHelper.MapPath(env, Constants.ROBOTS_PATH);
|
||||
return File(Constants.ROBOTS_PATH, "text/plain");
|
||||
}
|
||||
|
||||
protected IActionResult GenerateActionResult(object json)
|
||||
{
|
||||
return GenerateActionResult(json, View());
|
||||
}
|
||||
|
||||
protected IActionResult GenerateActionResult(object json, IActionResult result)
|
||||
{
|
||||
if (Request.IsAjaxRequest())
|
||||
{
|
||||
return Json(json);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
237
IdentityServer/Controllers/ErrorController.cs
Normal file
237
IdentityServer/Controllers/ErrorController.cs
Normal file
@ -0,0 +1,237 @@
|
||||
using IdentityServer4.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
using Teknik.Logging;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
public class ErrorController : DefaultController
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
|
||||
public ErrorController(ILogger<Logger> logger, Config config, IIdentityServerInteractionService interaction) : base(logger, config)
|
||||
{
|
||||
_interaction = interaction;
|
||||
}
|
||||
|
||||
public IActionResult HttpError(int statusCode)
|
||||
{
|
||||
switch (statusCode)
|
||||
{
|
||||
case 401:
|
||||
return Http401();
|
||||
case 403:
|
||||
return Http403();
|
||||
case 404:
|
||||
return Http404();
|
||||
default:
|
||||
return HttpGeneral(statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult HttpGeneral(int statusCode)
|
||||
{
|
||||
ViewBag.Title = statusCode;
|
||||
|
||||
LogError(LogLevel.Error, "HTTP Error Code: " + statusCode);
|
||||
|
||||
ErrorViewModel model = new ErrorViewModel();
|
||||
model.StatusCode = statusCode;
|
||||
|
||||
return GenerateActionResult(CreateErrorObj("Http", statusCode, "Invalid HTTP Response"), View("~/Views/Error/HttpGeneral.cshtml", model));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public IActionResult Http401()
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
|
||||
ViewBag.Title = "401";
|
||||
ViewBag.Description = "Unauthorized";
|
||||
|
||||
LogError(LogLevel.Error, "Unauthorized");
|
||||
|
||||
ErrorViewModel model = new ErrorViewModel();
|
||||
model.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
|
||||
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status401Unauthorized, "Unauthorized"), View("~/Views/Error/Http401.cshtml", model));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public IActionResult Http403()
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
|
||||
ViewBag.Title = "403";
|
||||
ViewBag.Description = "Access Denied";
|
||||
|
||||
LogError(LogLevel.Error, "Access Denied");
|
||||
|
||||
ErrorViewModel model = new ErrorViewModel();
|
||||
model.StatusCode = StatusCodes.Status403Forbidden;
|
||||
|
||||
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status403Forbidden, "Access Denied"), View("~/Views/Error/Http403.cshtml", model));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public IActionResult Http404()
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
|
||||
ViewBag.Title = "404";
|
||||
ViewBag.Description = "Uh Oh, can't find it!";
|
||||
|
||||
LogError(LogLevel.Warning, "Page Not Found");
|
||||
|
||||
ErrorViewModel model = new ErrorViewModel();
|
||||
model.StatusCode = StatusCodes.Status404NotFound;
|
||||
|
||||
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status404NotFound, "Page Not Found"), View("~/Views/Error/Http404.cshtml", model));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public IActionResult Http500(Exception exception)
|
||||
{
|
||||
if (HttpContext != null)
|
||||
{
|
||||
var ex = HttpContext.Features.Get<IExceptionHandlerFeature>();
|
||||
if (ex != null)
|
||||
{
|
||||
exception = ex.Error;
|
||||
}
|
||||
HttpContext.Session.Set("Exception", exception);
|
||||
}
|
||||
|
||||
Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
|
||||
ViewBag.Title = "500";
|
||||
ViewBag.Description = "Something Borked";
|
||||
|
||||
LogError(LogLevel.Error, "Server Error", exception);
|
||||
|
||||
ErrorViewModel model = new ErrorViewModel();
|
||||
model.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
model.Exception = exception;
|
||||
|
||||
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status500InternalServerError, exception.Message), View("~/Views/Error/Http500.cshtml", model));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> IdentityError(string errorId)
|
||||
{
|
||||
var message = await _interaction.GetErrorContextAsync(errorId);
|
||||
|
||||
Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
|
||||
ViewBag.Title = "Identity Error";
|
||||
ViewBag.Description = "The Identity Service threw an error";
|
||||
|
||||
LogError(LogLevel.Error, "Identity Error: " + message.Error);
|
||||
|
||||
IdentityErrorViewModel model = new IdentityErrorViewModel();
|
||||
model.Title = message.Error;
|
||||
model.Description = message.ErrorDescription;
|
||||
|
||||
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status500InternalServerError, message.Error), View("~/Views/Error/IdentityError.cshtml", model));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult SubmitErrorReport(SubmitReportViewModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
string exceptionMsg = model.Exception;
|
||||
|
||||
// Try to grab the actual exception that occured
|
||||
Exception ex = HttpContext.Session.Get<Exception>("Exception");
|
||||
if (ex != null)
|
||||
{
|
||||
exceptionMsg = string.Format(@"
|
||||
Exception: {0}
|
||||
|
||||
Source: {1}
|
||||
|
||||
Stack Trace:
|
||||
|
||||
{2}
|
||||
", ex.GetFullMessage(true), ex.Source, ex.StackTrace);
|
||||
}
|
||||
|
||||
// Let's also email the message to support
|
||||
SmtpClient client = new SmtpClient();
|
||||
client.Host = _config.ContactConfig.EmailAccount.Host;
|
||||
client.Port = _config.ContactConfig.EmailAccount.Port;
|
||||
client.EnableSsl = _config.ContactConfig.EmailAccount.SSL;
|
||||
client.DeliveryMethod = SmtpDeliveryMethod.Network;
|
||||
client.UseDefaultCredentials = true;
|
||||
client.Credentials = new System.Net.NetworkCredential(_config.ContactConfig.EmailAccount.Username, _config.ContactConfig.EmailAccount.Password);
|
||||
client.Timeout = 5000;
|
||||
|
||||
MailMessage mail = new MailMessage(new MailAddress(_config.NoReplyEmail, _config.NoReplyEmail), new MailAddress(_config.SupportEmail, "Teknik Support"));
|
||||
mail.Sender = new MailAddress(_config.ContactConfig.EmailAccount.EmailAddress);
|
||||
mail.Subject = "[Exception] Application Exception Occured";
|
||||
mail.Body = @"
|
||||
An exception has occured at: " + model.CurrentUrl + @"
|
||||
|
||||
----------------------------------------
|
||||
User Message:
|
||||
|
||||
" + model.Message + @"
|
||||
|
||||
----------------------------------------
|
||||
" + exceptionMsg;
|
||||
mail.BodyEncoding = UTF8Encoding.UTF8;
|
||||
mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;
|
||||
|
||||
client.Send(mail);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { error = "Error submitting report. Exception: " + ex.Message });
|
||||
}
|
||||
|
||||
return Json(new { result = "true" });
|
||||
}
|
||||
|
||||
private object CreateErrorObj(string type, int statusCode, string message)
|
||||
{
|
||||
return new { error = new { type = type, status = statusCode, message = message } };
|
||||
}
|
||||
|
||||
private void LogError(LogLevel level, string message)
|
||||
{
|
||||
LogError(level, message, null);
|
||||
}
|
||||
|
||||
private void LogError(LogLevel level, string message, Exception exception)
|
||||
{
|
||||
if (Request != null)
|
||||
{
|
||||
message += " | Url: " + Request.GetDisplayUrl();
|
||||
|
||||
message += " | Referred Url: " + Request.Headers["Referer"].ToString();
|
||||
|
||||
message += " | Method: " + Request.Method;
|
||||
|
||||
message += " | User Agent: " + Request.Headers["User-Agent"].ToString();
|
||||
}
|
||||
|
||||
_logger.Log(level, message, exception);
|
||||
}
|
||||
}
|
||||
}
|
93
IdentityServer/Controllers/GrantsController.cs
Normal file
93
IdentityServer/Controllers/GrantsController.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Teknik.IdentityServer.Security;
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
using Teknik.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Teknik.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// This sample controller allows a user to revoke grants given to clients
|
||||
/// </summary>
|
||||
[Authorize(AuthenticationSchemes = "Identity.Application")]
|
||||
public class GrantsController : DefaultController
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IClientStore _clients;
|
||||
private readonly IResourceStore _resources;
|
||||
|
||||
public GrantsController(
|
||||
ILogger<Logger> logger,
|
||||
Config config,
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clients,
|
||||
IResourceStore resources) : base(logger, config)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_clients = clients;
|
||||
_resources = resources;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show list of grants
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
ViewBag.Title = "Granted Applications";
|
||||
return View("Index", await BuildViewModelAsync());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle postback to revoke a client
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Revoke(string clientId)
|
||||
{
|
||||
await _interaction.RevokeUserConsentAsync(clientId);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
private async Task<GrantsViewModel> BuildViewModelAsync()
|
||||
{
|
||||
var grants = await _interaction.GetAllUserConsentsAsync();
|
||||
|
||||
var list = new List<GrantViewModel>();
|
||||
foreach(var grant in grants)
|
||||
{
|
||||
var client = await _clients.FindClientByIdAsync(grant.ClientId);
|
||||
if (client != null)
|
||||
{
|
||||
var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes);
|
||||
|
||||
var item = new GrantViewModel()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
ClientName = client.ClientName ?? client.ClientId,
|
||||
ClientLogoUrl = client.LogoUri,
|
||||
ClientUrl = client.ClientUri,
|
||||
Created = grant.CreationTime,
|
||||
Expires = grant.Expiration,
|
||||
IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(),
|
||||
ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray()
|
||||
};
|
||||
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return new GrantsViewModel
|
||||
{
|
||||
Grants = list
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
29
IdentityServer/Controllers/HomeController.cs
Normal file
29
IdentityServer/Controllers/HomeController.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using IdentityServer4.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.Security;
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
using Teknik.Logging;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
public class HomeController : DefaultController
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
|
||||
public HomeController(
|
||||
ILogger<Logger> logger,
|
||||
Config config,
|
||||
IIdentityServerInteractionService interaction) : base(logger, config)
|
||||
{
|
||||
_interaction = interaction;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
611
IdentityServer/Controllers/ManageController.cs
Normal file
611
IdentityServer/Controllers/ManageController.cs
Normal file
@ -0,0 +1,611 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.EntityFramework.DbContexts;
|
||||
using IdentityServer4.EntityFramework.Entities;
|
||||
using IdentityServer4.EntityFramework.Mappers;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using Teknik.IdentityServer.Models.Manage;
|
||||
using Teknik.Logging;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Controllers
|
||||
{
|
||||
[Authorize(Policy = "Internal", AuthenticationSchemes = "Bearer")]
|
||||
[Route("[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class ManageController : DefaultController
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public ManageController(
|
||||
ILogger<Logger> logger,
|
||||
Config config,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager) : base(logger, config)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateUser(NewUserModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
if (string.IsNullOrEmpty(model.Password))
|
||||
return new JsonResult(new { success = false, message = "Password is required" });
|
||||
|
||||
var identityUser = new ApplicationUser(model.Username)
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
UserName = model.Username,
|
||||
AccountStatus = model.AccountStatus,
|
||||
AccountType = model.AccountType,
|
||||
Email = model.RecoveryEmail,
|
||||
EmailConfirmed = model.RecoveryVerified,
|
||||
PGPPublicKey = model.PGPPublicKey
|
||||
};
|
||||
var result = await _userManager.CreateAsync(identityUser, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return new JsonResult(new { success = true });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Unable to create user.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DeleteUser(DeleteUserModel model, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
// Find this user's clients
|
||||
var foundClients = configContext.Clients.Where(c =>
|
||||
c.Properties.Exists(p =>
|
||||
p.Key == "username" &&
|
||||
p.Value.ToLower() == model.Username.ToLower())
|
||||
).ToList();
|
||||
if (foundClients != null)
|
||||
{
|
||||
configContext.Clients.RemoveRange(foundClients);
|
||||
configContext.SaveChanges();
|
||||
}
|
||||
|
||||
var result = await _userManager.DeleteAsync(foundUser);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to delete user.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> UserExists(string username)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(username);
|
||||
return new JsonResult(new { success = true, data = foundUser != null });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetUserInfo(string username)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
return new JsonResult(new { success = true, data = foundUser.ToJson() });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CheckPassword(CheckPasswordModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
if (string.IsNullOrEmpty(model.Password))
|
||||
return new JsonResult(new { success = false, message = "Password is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
bool valid = await _userManager.CheckPasswordAsync(foundUser, model.Password);
|
||||
return new JsonResult(new { success = true, data = valid });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> GeneratePasswordResetToken(GeneratePasswordResetTokenModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
string token = await _userManager.GeneratePasswordResetTokenAsync(foundUser);
|
||||
return new JsonResult(new { success = true, data = token });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
if (string.IsNullOrEmpty(model.Token))
|
||||
return new JsonResult(new { success = false, message = "Token is required" });
|
||||
if (string.IsNullOrEmpty(model.Password))
|
||||
return new JsonResult(new { success = false, message = "Password is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
var result = await _userManager.ResetPasswordAsync(foundUser, model.Token, model.Password);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to reset password.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdatePassword(UpdatePasswordModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
if (string.IsNullOrEmpty(model.CurrentPassword))
|
||||
return new JsonResult(new { success = false, message = "Current Password is required" });
|
||||
if (string.IsNullOrEmpty(model.NewPassword))
|
||||
return new JsonResult(new { success = false, message = "New Password is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
var result = await _userManager.ChangePasswordAsync(foundUser, model.CurrentPassword, model.NewPassword);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to update password.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdateEmail(UpdateEmailModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
var result = await _userManager.SetEmailAsync(foundUser, model.Email);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var token = await _userManager.GenerateEmailConfirmationTokenAsync(foundUser);
|
||||
return new JsonResult(new { success = true, data = token });
|
||||
}
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to update email address.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> VerifyEmail(VerifyEmailModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
if (string.IsNullOrEmpty(model.Token))
|
||||
return new JsonResult(new { success = false, message = "Token is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
var result = await _userManager.ConfirmEmailAsync(foundUser, model.Token);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to verify email address.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdateAccountStatus(UpdateAccountStatusModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
foundUser.AccountStatus = model.AccountStatus;
|
||||
|
||||
var result = await _userManager.UpdateAsync(foundUser);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to update account status.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdateAccountType(UpdateAccountTypeModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
foundUser.AccountType = model.AccountType;
|
||||
|
||||
var result = await _userManager.UpdateAsync(foundUser);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to update account type.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdatePGPPublicKey(UpdatePGPPublicKeyModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
foundUser.PGPPublicKey = model.PGPPublicKey;
|
||||
|
||||
var result = await _userManager.UpdateAsync(foundUser);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to update pgp public key.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Get2FAKey(string username)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser);
|
||||
|
||||
return new JsonResult(new { success = true, data = FormatKey(unformattedKey) });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Reset2FAKey(Reset2FAKeyModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
await _userManager.ResetAuthenticatorKeyAsync(foundUser);
|
||||
string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser);
|
||||
|
||||
return new JsonResult(new { success = true, data = FormatKey(unformattedKey) });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Enable2FA(Enable2FAModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
if (string.IsNullOrEmpty(model.Code))
|
||||
return new JsonResult(new { success = false, message = "Code is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
// Strip spaces and hypens
|
||||
var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
|
||||
|
||||
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
|
||||
foundUser, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
|
||||
|
||||
if (is2faTokenValid)
|
||||
{
|
||||
var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10);
|
||||
return new JsonResult(new { success = true, data = recoveryCodes.ToArray() });
|
||||
}
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to set Two-Factor Authentication.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Verification code is invalid." });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Disable2FA(Disable2FAModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, false);
|
||||
if (result.Succeeded)
|
||||
return new JsonResult(new { success = true });
|
||||
else
|
||||
return new JsonResult(new { success = false, message = "Unable to disable Two-Factor Authentication.", identityErrors = result.Errors });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> GenerateRecoveryCodes(GenerateRecoveryCodesModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||
if (foundUser != null)
|
||||
{
|
||||
if (foundUser.TwoFactorEnabled)
|
||||
{
|
||||
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10);
|
||||
|
||||
return new JsonResult(new { success = true, data = recoveryCodes.ToArray() });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Two-Factor Authentication is not enabled." });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetClient(string username, string clientId, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
if (string.IsNullOrEmpty(clientId))
|
||||
return new JsonResult(new { success = false, message = "Client Id is required" });
|
||||
|
||||
var client = configContext.Clients.FirstOrDefault(c =>
|
||||
c.ClientId == clientId &&
|
||||
c.Properties.Exists(p =>
|
||||
p.Key == "username" &&
|
||||
p.Value.ToLower() == username.ToLower())
|
||||
);
|
||||
if (client != null)
|
||||
{
|
||||
var foundClient = await clientStore.FindClientByIdAsync(client.ClientId);
|
||||
return new JsonResult(new { success = true, data = foundClient });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Client does not exist." });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetClients(string username, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return new JsonResult(new { success = false, message = "Username is required" });
|
||||
|
||||
var foundClientIds = configContext.Clients.Where(c =>
|
||||
c.Properties.Exists(p =>
|
||||
p.Key == "username" &&
|
||||
p.Value.ToLower() == username.ToLower())
|
||||
).Select(c => c.ClientId);
|
||||
var clients = new List<IdentityServer4.Models.Client>();
|
||||
foreach (var clientId in foundClientIds)
|
||||
{
|
||||
var foundClient = await clientStore.FindClientByIdAsync(clientId);
|
||||
if (foundClient != null)
|
||||
clients.Add(foundClient);
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = true, data = clients });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult CreateClient(CreateClientModel model, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
// Generate a unique client ID
|
||||
var clientId = StringHelper.RandomString(20, "abcdefghjkmnpqrstuvwxyz1234567890");
|
||||
while (configContext.Clients.Where(c => c.ClientId == clientId).FirstOrDefault() != null)
|
||||
{
|
||||
clientId = StringHelper.RandomString(20, "abcdefghjkmnpqrstuvwxyz1234567890");
|
||||
}
|
||||
|
||||
var clientSecret = StringHelper.RandomString(40, "abcdefghjkmnpqrstuvwxyz1234567890");
|
||||
|
||||
// Generate the origin for the callback
|
||||
Uri redirect = new Uri(model.CallbackUrl);
|
||||
string origin = redirect.Scheme + "://" + redirect.Host;
|
||||
|
||||
var client = new IdentityServer4.Models.Client
|
||||
{
|
||||
Properties = new Dictionary<string, string>()
|
||||
{
|
||||
{ "username", model.Username }
|
||||
},
|
||||
ClientId = clientId,
|
||||
ClientName = model.Name,
|
||||
ClientUri = model.HomepageUrl,
|
||||
LogoUri = model.LogoUrl,
|
||||
AllowedGrantTypes = new List<string>()
|
||||
{
|
||||
GrantType.AuthorizationCode,
|
||||
GrantType.ClientCredentials
|
||||
},
|
||||
|
||||
ClientSecrets =
|
||||
{
|
||||
new IdentityServer4.Models.Secret(clientSecret.Sha256())
|
||||
},
|
||||
|
||||
RequireConsent = true,
|
||||
|
||||
RedirectUris =
|
||||
{
|
||||
model.CallbackUrl
|
||||
},
|
||||
|
||||
AllowedCorsOrigins =
|
||||
{
|
||||
origin
|
||||
},
|
||||
|
||||
AllowedScopes = model.AllowedScopes,
|
||||
|
||||
AllowOfflineAccess = true
|
||||
};
|
||||
|
||||
configContext.Clients.Add(client.ToEntity());
|
||||
configContext.SaveChanges();
|
||||
|
||||
return new JsonResult(new { success = true, data = new { id = clientId, secret = clientSecret } });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult EditClient(EditClientModel model, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
// Validate it's an actual client
|
||||
var foundClient = configContext.Clients.Where(c => c.ClientId == model.ClientId).FirstOrDefault();
|
||||
if (foundClient != null)
|
||||
{
|
||||
foundClient.ClientName = model.Name;
|
||||
foundClient.ClientUri = model.HomepageUrl;
|
||||
foundClient.LogoUri = model.LogoUrl;
|
||||
configContext.Entry(foundClient).State = EntityState.Modified;
|
||||
|
||||
// Update the redirect URL for this client
|
||||
var results = configContext.Set<ClientRedirectUri>().Where(c => c.ClientId == foundClient.Id).ToList();
|
||||
if (results != null)
|
||||
{
|
||||
configContext.RemoveRange(results);
|
||||
}
|
||||
var newUri = new ClientRedirectUri();
|
||||
newUri.Client = foundClient;
|
||||
newUri.ClientId = foundClient.Id;
|
||||
newUri.RedirectUri = model.CallbackUrl;
|
||||
configContext.Add(newUri);
|
||||
|
||||
// Generate the origin for the callback
|
||||
Uri redirect = new Uri(model.CallbackUrl);
|
||||
string origin = redirect.Scheme + "://" + redirect.Host;
|
||||
|
||||
// Update the allowed origin for this client
|
||||
var corsOrigins = configContext.Set<ClientCorsOrigin>().Where(c => c.ClientId == foundClient.Id).ToList();
|
||||
if (corsOrigins != null)
|
||||
{
|
||||
configContext.RemoveRange(corsOrigins);
|
||||
}
|
||||
var newOrigin = new ClientCorsOrigin();
|
||||
newOrigin.Client = foundClient;
|
||||
newOrigin.ClientId = foundClient.Id;
|
||||
newOrigin.Origin = origin;
|
||||
configContext.Add(newUri);
|
||||
|
||||
// Save all the changed
|
||||
configContext.SaveChanges();
|
||||
|
||||
return new JsonResult(new { success = true });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Client does not exist." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult DeleteClient(DeleteClientModel model, [FromServices] ConfigurationDbContext configContext)
|
||||
{
|
||||
var foundClient = configContext.Clients.Where(c => c.ClientId == model.ClientId).FirstOrDefault();
|
||||
if (foundClient != null)
|
||||
{
|
||||
configContext.Clients.Remove(foundClient);
|
||||
configContext.SaveChanges();
|
||||
|
||||
return new JsonResult(new { success = true });
|
||||
}
|
||||
|
||||
return new JsonResult(new { success = false, message = "Client does not exist." });
|
||||
}
|
||||
|
||||
private string FormatKey(string unformattedKey)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
int currentPosition = 0;
|
||||
while (currentPosition + 4 < unformattedKey.Length)
|
||||
{
|
||||
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
|
||||
currentPosition += 4;
|
||||
}
|
||||
if (currentPosition < unformattedKey.Length)
|
||||
{
|
||||
result.Append(unformattedKey.Substring(currentPosition));
|
||||
}
|
||||
|
||||
return result.ToString().ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,242 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Teknik.IdentityServer;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20181015060219_InitialApplicationDbContextMigration")]
|
||||
partial class InitialApplicationDbContextMigration
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Teknik.IdentityServer.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<int>("AccountStatus");
|
||||
|
||||
b.Property<int>("AccountType");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<DateTime>("CreationDate");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<DateTime>("LastSeen");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PGPPublicKey");
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
|
||||
{
|
||||
public partial class InitialApplicationDbContextMigration : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false),
|
||||
CreationDate = table.Column<DateTime>(nullable: false),
|
||||
LastSeen = table.Column<DateTime>(nullable: false),
|
||||
AccountType = table.Column<int>(nullable: false),
|
||||
AccountStatus = table.Column<int>(nullable: false),
|
||||
PGPPublicKey = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(nullable: false),
|
||||
ProviderKey = table.Column<string>(nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true,
|
||||
filter: "[NormalizedName] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true,
|
||||
filter: "[NormalizedUserName] IS NOT NULL");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Teknik.IdentityServer;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Teknik.IdentityServer.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<int>("AccountStatus");
|
||||
|
||||
b.Property<int>("AccountType");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<DateTime>("CreationDate");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<DateTime>("LastSeen");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PGPPublicKey");
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,679 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IdentityServer4.EntityFramework.DbContexts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
|
||||
{
|
||||
[DbContext(typeof(ConfigurationDbContext))]
|
||||
[Migration("20180930040544_InitialIdentityServerConfigurationDbMigration")]
|
||||
partial class InitialIdentityServerConfigurationDbMigration
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<DateTime?>("LastAccessed");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Updated");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ApiResources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Emphasize");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Required");
|
||||
|
||||
b.Property<bool>("ShowInDiscoveryDocument");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ApiScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiScopeId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiScopeId");
|
||||
|
||||
b.ToTable("ApiScopeClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("AbsoluteRefreshTokenLifetime");
|
||||
|
||||
b.Property<int>("AccessTokenLifetime");
|
||||
|
||||
b.Property<int>("AccessTokenType");
|
||||
|
||||
b.Property<bool>("AllowAccessTokensViaBrowser");
|
||||
|
||||
b.Property<bool>("AllowOfflineAccess");
|
||||
|
||||
b.Property<bool>("AllowPlainTextPkce");
|
||||
|
||||
b.Property<bool>("AllowRememberConsent");
|
||||
|
||||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken");
|
||||
|
||||
b.Property<bool>("AlwaysSendClientClaims");
|
||||
|
||||
b.Property<int>("AuthorizationCodeLifetime");
|
||||
|
||||
b.Property<bool>("BackChannelLogoutSessionRequired");
|
||||
|
||||
b.Property<string>("BackChannelLogoutUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<string>("ClientClaimsPrefix")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<int?>("ConsentLifetime");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<bool>("EnableLocalLogin");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<bool>("FrontChannelLogoutSessionRequired");
|
||||
|
||||
b.Property<string>("FrontChannelLogoutUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<int>("IdentityTokenLifetime");
|
||||
|
||||
b.Property<bool>("IncludeJwtId");
|
||||
|
||||
b.Property<DateTime?>("LastAccessed");
|
||||
|
||||
b.Property<string>("LogoUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<string>("PairWiseSubjectSalt")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ProtocolType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<int>("RefreshTokenExpiration");
|
||||
|
||||
b.Property<int>("RefreshTokenUsage");
|
||||
|
||||
b.Property<bool>("RequireClientSecret");
|
||||
|
||||
b.Property<bool>("RequireConsent");
|
||||
|
||||
b.Property<bool>("RequirePkce");
|
||||
|
||||
b.Property<int>("SlidingRefreshTokenLifetime");
|
||||
|
||||
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh");
|
||||
|
||||
b.Property<DateTime?>("Updated");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Clients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Origin")
|
||||
.IsRequired()
|
||||
.HasMaxLength(150);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientCorsOrigins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("GrantType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientGrantTypes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientIdPRestrictions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("PostLogoutRedirectUri")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientPostLogoutRedirectUris");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientRedirectUris");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Emphasize");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Required");
|
||||
|
||||
b.Property<bool>("ShowInDiscoveryDocument");
|
||||
|
||||
b.Property<DateTime?>("Updated");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("IdentityResources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Scopes")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ApiScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Secrets")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("Claims")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedCorsOrigins")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedGrantTypes")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("IdentityProviderRestrictions")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("PostLogoutRedirectUris")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("RedirectUris")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedScopes")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("ClientSecrets")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,602 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
|
||||
{
|
||||
public partial class InitialIdentityServerConfigurationDbMigration : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiResources",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Enabled = table.Column<bool>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
Updated = table.Column<DateTime>(nullable: true),
|
||||
LastAccessed = table.Column<DateTime>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiResources", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Clients",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Enabled = table.Column<bool>(nullable: false),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ProtocolType = table.Column<string>(maxLength: 200, nullable: false),
|
||||
RequireClientSecret = table.Column<bool>(nullable: false),
|
||||
ClientName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
ClientUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
LogoUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
RequireConsent = table.Column<bool>(nullable: false),
|
||||
AllowRememberConsent = table.Column<bool>(nullable: false),
|
||||
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(nullable: false),
|
||||
RequirePkce = table.Column<bool>(nullable: false),
|
||||
AllowPlainTextPkce = table.Column<bool>(nullable: false),
|
||||
AllowAccessTokensViaBrowser = table.Column<bool>(nullable: false),
|
||||
FrontChannelLogoutUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
FrontChannelLogoutSessionRequired = table.Column<bool>(nullable: false),
|
||||
BackChannelLogoutUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
BackChannelLogoutSessionRequired = table.Column<bool>(nullable: false),
|
||||
AllowOfflineAccess = table.Column<bool>(nullable: false),
|
||||
IdentityTokenLifetime = table.Column<int>(nullable: false),
|
||||
AccessTokenLifetime = table.Column<int>(nullable: false),
|
||||
AuthorizationCodeLifetime = table.Column<int>(nullable: false),
|
||||
ConsentLifetime = table.Column<int>(nullable: true),
|
||||
AbsoluteRefreshTokenLifetime = table.Column<int>(nullable: false),
|
||||
SlidingRefreshTokenLifetime = table.Column<int>(nullable: false),
|
||||
RefreshTokenUsage = table.Column<int>(nullable: false),
|
||||
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(nullable: false),
|
||||
RefreshTokenExpiration = table.Column<int>(nullable: false),
|
||||
AccessTokenType = table.Column<int>(nullable: false),
|
||||
EnableLocalLogin = table.Column<bool>(nullable: false),
|
||||
IncludeJwtId = table.Column<bool>(nullable: false),
|
||||
AlwaysSendClientClaims = table.Column<bool>(nullable: false),
|
||||
ClientClaimsPrefix = table.Column<string>(maxLength: 200, nullable: true),
|
||||
PairWiseSubjectSalt = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
Updated = table.Column<DateTime>(nullable: true),
|
||||
LastAccessed = table.Column<DateTime>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Clients", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IdentityResources",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Enabled = table.Column<bool>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Required = table.Column<bool>(nullable: false),
|
||||
Emphasize = table.Column<bool>(nullable: false),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
Updated = table.Column<DateTime>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IdentityResources", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiClaims_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiProperties", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiProperties_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Required = table.Column<bool>(nullable: false),
|
||||
Emphasize = table.Column<bool>(nullable: false),
|
||||
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiScopes_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiSecrets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||
Value = table.Column<string>(maxLength: 4000, nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
ApiResourceId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiSecrets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiSecrets_ApiResources_ApiResourceId",
|
||||
column: x => x.ApiResourceId,
|
||||
principalTable: "ApiResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 250, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientClaims_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientCorsOrigins",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Origin = table.Column<string>(maxLength: 150, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientCorsOrigins_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientGrantTypes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
GrantType = table.Column<string>(maxLength: 250, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientGrantTypes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientGrantTypes_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientIdPRestrictions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Provider = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientIdPRestrictions_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientPostLogoutRedirectUris",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
PostLogoutRedirectUri = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientProperties", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientProperties_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientRedirectUris",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
RedirectUri = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientRedirectUris", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientRedirectUris_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientScopes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Scope = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientScopes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientScopes_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ClientSecrets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Description = table.Column<string>(maxLength: 2000, nullable: true),
|
||||
Value = table.Column<string>(maxLength: 4000, nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Created = table.Column<DateTime>(nullable: false),
|
||||
ClientId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ClientSecrets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ClientSecrets_Clients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "Clients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IdentityClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||
IdentityResourceId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IdentityClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_IdentityClaims_IdentityResources_IdentityResourceId",
|
||||
column: x => x.IdentityResourceId,
|
||||
principalTable: "IdentityResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IdentityProperties",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||
IdentityResourceId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IdentityProperties", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_IdentityProperties_IdentityResources_IdentityResourceId",
|
||||
column: x => x.IdentityResourceId,
|
||||
principalTable: "IdentityResources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ApiScopeClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||
ApiScopeId = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ApiScopeClaims_ApiScopes_ApiScopeId",
|
||||
column: x => x.ApiScopeId,
|
||||
principalTable: "ApiScopes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiClaims_ApiResourceId",
|
||||
table: "ApiClaims",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiProperties_ApiResourceId",
|
||||
table: "ApiProperties",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiResources_Name",
|
||||
table: "ApiResources",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopeClaims_ApiScopeId",
|
||||
table: "ApiScopeClaims",
|
||||
column: "ApiScopeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopes_ApiResourceId",
|
||||
table: "ApiScopes",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiScopes_Name",
|
||||
table: "ApiScopes",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApiSecrets_ApiResourceId",
|
||||
table: "ApiSecrets",
|
||||
column: "ApiResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientClaims_ClientId",
|
||||
table: "ClientClaims",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientCorsOrigins_ClientId",
|
||||
table: "ClientCorsOrigins",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientGrantTypes_ClientId",
|
||||
table: "ClientGrantTypes",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientIdPRestrictions_ClientId",
|
||||
table: "ClientIdPRestrictions",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientPostLogoutRedirectUris_ClientId",
|
||||
table: "ClientPostLogoutRedirectUris",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientProperties_ClientId",
|
||||
table: "ClientProperties",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientRedirectUris_ClientId",
|
||||
table: "ClientRedirectUris",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Clients_ClientId",
|
||||
table: "Clients",
|
||||
column: "ClientId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientScopes_ClientId",
|
||||
table: "ClientScopes",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ClientSecrets_ClientId",
|
||||
table: "ClientSecrets",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IdentityClaims_IdentityResourceId",
|
||||
table: "IdentityClaims",
|
||||
column: "IdentityResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IdentityProperties_IdentityResourceId",
|
||||
table: "IdentityProperties",
|
||||
column: "IdentityResourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IdentityResources_Name",
|
||||
table: "IdentityResources",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiProperties");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiScopeClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiSecrets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientCorsOrigins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientGrantTypes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientIdPRestrictions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientPostLogoutRedirectUris");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientProperties");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientRedirectUris");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientScopes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ClientSecrets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IdentityClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IdentityProperties");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiScopes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Clients");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IdentityResources");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ApiResources");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,677 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IdentityServer4.EntityFramework.DbContexts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
|
||||
{
|
||||
[DbContext(typeof(ConfigurationDbContext))]
|
||||
partial class ConfigurationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<DateTime?>("LastAccessed");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Updated");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ApiResources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Emphasize");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Required");
|
||||
|
||||
b.Property<bool>("ShowInDiscoveryDocument");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ApiScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiScopeId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiScopeId");
|
||||
|
||||
b.ToTable("ApiScopeClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiResourceId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApiResourceId");
|
||||
|
||||
b.ToTable("ApiSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("AbsoluteRefreshTokenLifetime");
|
||||
|
||||
b.Property<int>("AccessTokenLifetime");
|
||||
|
||||
b.Property<int>("AccessTokenType");
|
||||
|
||||
b.Property<bool>("AllowAccessTokensViaBrowser");
|
||||
|
||||
b.Property<bool>("AllowOfflineAccess");
|
||||
|
||||
b.Property<bool>("AllowPlainTextPkce");
|
||||
|
||||
b.Property<bool>("AllowRememberConsent");
|
||||
|
||||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken");
|
||||
|
||||
b.Property<bool>("AlwaysSendClientClaims");
|
||||
|
||||
b.Property<int>("AuthorizationCodeLifetime");
|
||||
|
||||
b.Property<bool>("BackChannelLogoutSessionRequired");
|
||||
|
||||
b.Property<string>("BackChannelLogoutUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<string>("ClientClaimsPrefix")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<int?>("ConsentLifetime");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<bool>("EnableLocalLogin");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<bool>("FrontChannelLogoutSessionRequired");
|
||||
|
||||
b.Property<string>("FrontChannelLogoutUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<int>("IdentityTokenLifetime");
|
||||
|
||||
b.Property<bool>("IncludeJwtId");
|
||||
|
||||
b.Property<DateTime?>("LastAccessed");
|
||||
|
||||
b.Property<string>("LogoUri")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<string>("PairWiseSubjectSalt")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ProtocolType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<int>("RefreshTokenExpiration");
|
||||
|
||||
b.Property<int>("RefreshTokenUsage");
|
||||
|
||||
b.Property<bool>("RequireClientSecret");
|
||||
|
||||
b.Property<bool>("RequireConsent");
|
||||
|
||||
b.Property<bool>("RequirePkce");
|
||||
|
||||
b.Property<int>("SlidingRefreshTokenLifetime");
|
||||
|
||||
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh");
|
||||
|
||||
b.Property<DateTime?>("Updated");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Clients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Origin")
|
||||
.IsRequired()
|
||||
.HasMaxLength(150);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientCorsOrigins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("GrantType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientGrantTypes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Provider")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientIdPRestrictions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("PostLogoutRedirectUri")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientPostLogoutRedirectUris");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientRedirectUris");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("ClientSecrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000);
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Emphasize");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<bool>("Required");
|
||||
|
||||
b.Property<bool>("ShowInDiscoveryDocument");
|
||||
|
||||
b.Property<DateTime?>("Updated");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("IdentityResources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("IdentityResourceId");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250);
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2000);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IdentityResourceId");
|
||||
|
||||
b.ToTable("IdentityProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Scopes")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("ApiScopeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||
.WithMany("Secrets")
|
||||
.HasForeignKey("ApiResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("Claims")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedCorsOrigins")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedGrantTypes")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("IdentityProviderRestrictions")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("PostLogoutRedirectUris")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("RedirectUris")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedScopes")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("ClientSecrets")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||
.WithMany("UserClaims")
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||
.WithMany("Properties")
|
||||
.HasForeignKey("IdentityResourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IdentityServer4.EntityFramework.DbContexts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
|
||||
{
|
||||
[DbContext(typeof(PersistedGrantDbContext))]
|
||||
[Migration("20180930040529_InitialIdentityServerPersistedGrantDbMigration")]
|
||||
partial class InitialIdentityServerPersistedGrantDbMigration
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
|
||||
{
|
||||
public partial class InitialIdentityServerPersistedGrantDbMigration : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersistedGrants");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IdentityServer4.EntityFramework.DbContexts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
|
||||
{
|
||||
[DbContext(typeof(PersistedGrantDbContext))]
|
||||
partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
54
IdentityServer/IdentityServer.csproj
Normal file
54
IdentityServer/IdentityServer.csproj
Normal file
@ -0,0 +1,54 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>Teknik.IdentityServer</AssemblyName>
|
||||
<RootNamespace>Teknik.IdentityServer</RootNamespace>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||
<Configurations>Debug;Release;Test</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Middleware\" />
|
||||
<Folder Include="App_Data\" />
|
||||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4" Version="2.3.0-preview1-update2" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.7.0-preview1" />
|
||||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.3.0-preview1-update2" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.3.0-preview1-update1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.5" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Configuration\Configuration.csproj" />
|
||||
<ProjectReference Include="..\Logging\Logging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="App_Data\Config.json">
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Images\favicon.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\logo-black.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\logo-blue.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
BIN
IdentityServer/Images/favicon.ico
Normal file
BIN
IdentityServer/Images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
23
IdentityServer/Images/logo-black.svg
Normal file
23
IdentityServer/Images/logo-black.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="263.292px" height="72.13px" viewBox="0 0 263.292 72.13" enable-background="new 0 0 263.292 72.13" xml:space="preserve">
|
||||
<path d="M6.386,37.689c0-3.666-2.722-6.7-6.386-7.538V22.3h6.386V10.784h11.62V22.3h18.217v10.363h-22.09v3.561
|
||||
c2.513,0.732,3.873,2.93,3.873,5.863v19.574h18.217V72.13H11.515c-2.616,0-5.129-2.197-5.129-5.234V37.689z"/>
|
||||
<path d="M130.21,61.662l-15.599-16.121l12.04-12.877h7.118V22.3h-8.689c-2.512,0-4.083,0.942-5.129,1.989l-15.39,16.854h-2.407V5.13
|
||||
c0-2.512-2.199-5.13-5.13-5.13H84.147v10.47h6.386v51.192h-5.895h-0.596H50.317V49.728h4.188c0.523,2.721,2.513,3.664,5.024,3.664
|
||||
h20.31c3.036,0,5.235-2.199,5.235-5.236V27.638c0-3.035-2.199-5.338-5.235-5.338H44.036c-2.722,0-5.34,2.303-5.34,5.338v39.258
|
||||
c0,3.037,2.618,5.234,5.34,5.234h40.007h1.03h11.951c2.931,0,5.13-2.197,5.13-5.234V50.984h3.035l17.798,19.264
|
||||
c1.569,1.465,3.036,1.883,5.024,1.883h5.758V61.662H130.21z M50.317,32.663h23.135v10.366H50.317V32.663z"/>
|
||||
<polygon points="203.416,0 196.745,0 191.794,0 191.794,12.772 203.416,12.772 "/>
|
||||
<path d="M199.955,61.662v-3.453c2.617-0.629,3.873-2.723,3.873-4.816V27.638c0-3.035-2.304-5.338-5.13-5.338h-12.98v10.363h6.489
|
||||
v24.184c0,3.559-2.512,3.635-6.489,4.682V72.13h24.602H223.3c2.932,0,5.13-2.197,5.13-5.234V50.984h3.035l17.798,19.264
|
||||
c1.57,1.465,3.036,1.883,5.024,1.883h9.004V61.662h-6.805l-15.6-16.121l12.04-12.877h7.119V22.3h-8.69
|
||||
c-2.512,0-4.082,0.942-5.129,1.989l-15.39,16.854h-2.407V5.13c0-2.512-2.198-5.13-5.13-5.13h-12.877v10.47h6.386v51.192"/>
|
||||
<path d="M183.249,61.662V27.638c0-3.035-2.721-5.338-5.548-5.338h-25.754l-4.71,7.747h-0.209v-2.514
|
||||
c0-2.093-1.676-5.233-5.444-5.233l-5.34-0.135v10.498v21.59v2.594V72.13h18.217V61.662h-10.469v-3.453
|
||||
c2.617-0.629,3.873-2.723,3.873-4.816V40.201c0-4.396,3.455-7.538,7.852-7.538h15.912v34.233c0,1.289,0.435,2.428,1.151,3.311
|
||||
c0.97,1.197,2.458,1.924,4.084,1.924h6.381L183.249,61.662L183.249,61.662z"/>
|
||||
<rect x="171.628" y="54.419" width="11.62" height="6.25"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
23
IdentityServer/Images/logo-blue.svg
Normal file
23
IdentityServer/Images/logo-blue.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="263.292px" height="72.13px" viewBox="0 0 263.292 72.13" enable-background="new 0 0 263.292 72.13" xml:space="preserve">
|
||||
<path fill="#418BCA" d="M6.386,37.689c0-3.666-2.722-6.7-6.386-7.538V22.3h6.386V10.784h11.62V22.3h18.217v10.363h-22.09v3.561
|
||||
c2.513,0.732,3.873,2.93,3.873,5.863v19.574h18.217V72.13H11.515c-2.616,0-5.129-2.197-5.129-5.234V37.689z"/>
|
||||
<path fill="#418BCA" d="M130.21,61.662l-15.599-16.121l12.04-12.877h7.118V22.3h-8.689c-2.512,0-4.083,0.942-5.129,1.989
|
||||
l-15.39,16.854h-2.407V5.13c0-2.512-2.199-5.13-5.13-5.13H84.147v10.47h6.386v51.192h-5.895h-0.596H50.317V49.728h4.188
|
||||
c0.523,2.721,2.513,3.664,5.024,3.664h20.31c3.036,0,5.235-2.199,5.235-5.236V27.638c0-3.035-2.199-5.338-5.235-5.338H44.036
|
||||
c-2.722,0-5.34,2.303-5.34,5.338v39.258c0,3.037,2.618,5.234,5.34,5.234h40.007h1.03h11.951c2.931,0,5.13-2.197,5.13-5.234V50.984
|
||||
h3.035l17.798,19.264c1.569,1.465,3.036,1.883,5.024,1.883h5.758V61.662H130.21z M50.317,32.663h23.135v10.366H50.317V32.663z"/>
|
||||
<polygon fill="#418BCA" points="203.416,0 196.745,0 191.794,0 191.794,12.772 203.416,12.772 "/>
|
||||
<path fill="#418BCA" d="M199.955,61.662v-3.453c2.617-0.629,3.873-2.723,3.873-4.816V27.638c0-3.035-2.304-5.338-5.13-5.338h-12.98
|
||||
v10.363h6.489v24.184c0,3.559-2.512,3.635-6.489,4.682V72.13h24.602H223.3c2.932,0,5.13-2.197,5.13-5.234V50.984h3.035
|
||||
l17.798,19.264c1.57,1.465,3.036,1.883,5.024,1.883h9.004V61.662h-6.805l-15.6-16.121l12.04-12.877h7.119V22.3h-8.69
|
||||
c-2.512,0-4.082,0.942-5.129,1.989l-15.39,16.854h-2.407V5.13c0-2.512-2.198-5.13-5.13-5.13h-12.877v10.47h6.386v51.192"/>
|
||||
<path fill="#418BCA" d="M183.249,61.662V27.638c0-3.035-2.721-5.338-5.548-5.338h-25.754l-4.71,7.747h-0.209v-2.514
|
||||
c0-2.093-1.676-5.233-5.444-5.233l-5.34-0.135v10.498v21.59v2.594V72.13h18.217V61.662h-10.469v-3.453
|
||||
c2.617-0.629,3.873-2.723,3.873-4.816V40.201c0-4.396,3.455-7.538,7.852-7.538h15.912v34.233c0,1.289,0.435,2.428,1.151,3.311
|
||||
c0.97,1.197,2.458,1.924,4.084,1.924h6.381L183.249,61.662L183.249,61.662z"/>
|
||||
<rect x="171.628" y="54.419" fill="#418BCA" width="11.62" height="6.25"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
118
IdentityServer/Middleware/BlacklistMiddleware.cs
Normal file
118
IdentityServer/Middleware/BlacklistMiddleware.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
public class BlacklistMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IMemoryCache _cache;
|
||||
|
||||
public BlacklistMiddleware(RequestDelegate next, IMemoryCache cache)
|
||||
{
|
||||
_next = next;
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context, Config config)
|
||||
{
|
||||
// Beggining of Request
|
||||
bool blocked = false;
|
||||
string blockReason = string.Empty;
|
||||
|
||||
#region Detect Blacklisted IPs
|
||||
if (!blocked)
|
||||
{
|
||||
string IPAddr = context.Request.HttpContext.Connection.RemoteIpAddress.ToString();
|
||||
if (!string.IsNullOrEmpty(IPAddr))
|
||||
{
|
||||
StringDictionary badIPs = GetFileData(context, "BlockedIPs", config.IPBlacklistFile);
|
||||
|
||||
blocked |= (badIPs != null && badIPs.ContainsKey(IPAddr));
|
||||
blockReason = $"This IP address ({IPAddr}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance.";
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Detect Blacklisted Referrers
|
||||
if (!blocked)
|
||||
{
|
||||
string referrer = context.Request.Headers["Referer"].ToString();
|
||||
if (!string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
StringDictionary badReferrers = GetFileData(context, "BlockedReferrers", config.ReferrerBlacklistFile);
|
||||
|
||||
blocked |= (badReferrers != null && badReferrers.ContainsKey(referrer));
|
||||
blockReason = $"This referrer ({referrer}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance.";
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (blocked)
|
||||
{
|
||||
// Clear the response
|
||||
context.Response.Clear();
|
||||
|
||||
string jsonResult = JsonConvert.SerializeObject(new { error = new { type = "Blacklist", message = blockReason } });
|
||||
await context.Response.WriteAsync(jsonResult);
|
||||
return;
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
|
||||
// End of request
|
||||
}
|
||||
|
||||
public StringDictionary GetFileData(HttpContext context, string key, string filePath)
|
||||
{
|
||||
StringDictionary data;
|
||||
if (!_cache.TryGetValue(key, out data))
|
||||
{
|
||||
data = GetFileLines(filePath);
|
||||
_cache.Set(key, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public StringDictionary GetFileLines(string configPath)
|
||||
{
|
||||
StringDictionary retval = new StringDictionary();
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(configPath))
|
||||
{
|
||||
String line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
line = line.Trim();
|
||||
if (line.Length != 0)
|
||||
{
|
||||
retval.Add(line, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BlacklistMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseBlacklist(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<BlacklistMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
72
IdentityServer/Middleware/CORSMiddleware.cs
Normal file
72
IdentityServer/Middleware/CORSMiddleware.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||
public class CORSMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public CORSMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public Task InvokeAsync(HttpContext httpContext, Config config)
|
||||
{
|
||||
// Allow this domain, or everything if local
|
||||
string origin = (httpContext.Request.IsLocal()) ? "*" : httpContext.Request.Headers["Origin"].ToString();
|
||||
|
||||
// Is the referrer set to the CDN and we are using a CDN?
|
||||
if (config.UseCdn && !string.IsNullOrEmpty(config.CdnHost))
|
||||
{
|
||||
try
|
||||
{
|
||||
string host = httpContext.Request.Headers["Host"];
|
||||
Uri uri = new Uri(config.CdnHost);
|
||||
if (host == uri.Host)
|
||||
origin = host;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
string domain = (string.IsNullOrEmpty(origin)) ? string.Empty : origin.GetDomain();
|
||||
|
||||
if (string.IsNullOrEmpty(origin))
|
||||
{
|
||||
string host = httpContext.Request.Headers["Host"];
|
||||
string sub = host.GetSubdomain();
|
||||
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (domain != config.Host)
|
||||
{
|
||||
string sub = origin.GetSubdomain();
|
||||
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host;
|
||||
}
|
||||
}
|
||||
|
||||
httpContext.Response.Headers.Append("Access-Control-Allow-Origin", origin);
|
||||
httpContext.Response.Headers.Append("Vary", "Origin");
|
||||
|
||||
return _next(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class CORSMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseCORS(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<CORSMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
76
IdentityServer/Middleware/CSPMiddleware.cs
Normal file
76
IdentityServer/Middleware/CSPMiddleware.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||
public class CSPMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public CSPMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public Task Invoke(HttpContext httpContext, Config config)
|
||||
{
|
||||
if (!httpContext.Request.IsLocal())
|
||||
{
|
||||
// Default to nothing allowed
|
||||
string allowedDomain = "'none'";
|
||||
|
||||
// Allow this domain
|
||||
string host = httpContext.Request.Headers["Host"];
|
||||
|
||||
if (!string.IsNullOrEmpty(host))
|
||||
{
|
||||
string domain = host.GetDomain();
|
||||
|
||||
allowedDomain = string.Format("*.{0} {0}", domain);
|
||||
}
|
||||
|
||||
var csp = string.Format(
|
||||
"default-src 'self'; " +
|
||||
"script-src blob: 'unsafe-eval' 'unsafe-inline' {0}; " +
|
||||
"style-src 'unsafe-inline' {0}; " +
|
||||
"img-src data: *; " +
|
||||
"font-src data: {0}; " +
|
||||
"connect-src wss: blob: data: {0}; " +
|
||||
"media-src *; " +
|
||||
"worker-src blob: mediastream: {0}; " +
|
||||
"form-action *; " +
|
||||
"base-uri {0}; " +
|
||||
"frame-ancestors {0};",
|
||||
allowedDomain);
|
||||
|
||||
if (!httpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
|
||||
{
|
||||
httpContext.Response.Headers.Add("Content-Security-Policy", csp);
|
||||
}
|
||||
// and once again for IE
|
||||
if (!httpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
|
||||
{
|
||||
httpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
|
||||
}
|
||||
}
|
||||
|
||||
return _next(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class CSPMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseCSP(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<CSPMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
105
IdentityServer/Middleware/ErrorHandlerMiddleware.cs
Normal file
105
IdentityServer/Middleware/ErrorHandlerMiddleware.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using IdentityServer4.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.Controllers;
|
||||
using Teknik.Logging;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
public class ErrorHandlerMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IRouter _router;
|
||||
|
||||
public ErrorHandlerMiddleware(RequestDelegate next, IRouter router)
|
||||
{
|
||||
_next = next;
|
||||
_router = router;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext, ILogger<Logger> logger, Config config, IIdentityServerInteractionService interaction)
|
||||
{
|
||||
var statusCodeFeature = new StatusCodePagesFeature();
|
||||
httpContext.Features.Set<IStatusCodePagesFeature>(statusCodeFeature);
|
||||
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
await _next(httpContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
httpContext.Response.StatusCode = 500;
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
if (!statusCodeFeature.Enabled)
|
||||
{
|
||||
// Check if the feature is still available because other middleware (such as a web API written in MVC) could
|
||||
// have disabled the feature to prevent HTML status code responses from showing up to an API client.
|
||||
return;
|
||||
}
|
||||
|
||||
// Do nothing if a response body has already been provided or not 404 response
|
||||
if (httpContext.Response.HasStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect if there is a response code or exception occured
|
||||
if ((httpContext.Response.StatusCode >= 400 && httpContext.Response.StatusCode <= 600) || exception != null)
|
||||
{
|
||||
RouteData routeData = new RouteData();
|
||||
routeData.Values.Add("controller", "Error");
|
||||
routeData.Routers.Add(_router);
|
||||
|
||||
var context = new ControllerContext();
|
||||
context.HttpContext = httpContext;
|
||||
context.RouteData = routeData;
|
||||
context.ActionDescriptor = new Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor();
|
||||
|
||||
ErrorController errorController = new ErrorController(logger, config, interaction);
|
||||
errorController.ControllerContext = context;
|
||||
|
||||
if (httpContext.Response.StatusCode == 500)
|
||||
{
|
||||
await errorController.Http500(exception).ExecuteResultAsync(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
await errorController.HttpError(httpContext.Response.StatusCode).ExecuteResultAsync(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class SetupErrorHandlerMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder, Config config)
|
||||
{
|
||||
var routes = new RouteBuilder(builder)
|
||||
{
|
||||
DefaultHandler = builder.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
|
||||
};
|
||||
|
||||
return builder.UseMiddleware<ErrorHandlerMiddleware>(routes.Build());
|
||||
}
|
||||
}
|
||||
}
|
68
IdentityServer/Middleware/PerformanceMonitorMiddleware.cs
Normal file
68
IdentityServer/Middleware/PerformanceMonitorMiddleware.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||
public class PerformanceMonitorMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public PerformanceMonitorMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext, Config config)
|
||||
{
|
||||
Stopwatch timer = new Stopwatch();
|
||||
timer.Start();
|
||||
|
||||
httpContext.Response.OnStarting(state =>
|
||||
{
|
||||
var context = (HttpContext)state;
|
||||
|
||||
timer.Stop();
|
||||
|
||||
double ms = (double)timer.ElapsedMilliseconds;
|
||||
string result = string.Format("{0:F0}", ms);
|
||||
|
||||
if (!httpContext.Response.Headers.IsReadOnly)
|
||||
httpContext.Response.Headers.Add("GenerationTime", result);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, httpContext);
|
||||
|
||||
await _next(httpContext);
|
||||
|
||||
// Don't interfere with non-HTML responses
|
||||
if (httpContext.Response.ContentType != null && httpContext.Response.ContentType.StartsWith("text/html") && httpContext.Response.StatusCode == 200 && !httpContext.Request.IsAjaxRequest())
|
||||
{
|
||||
double ms = (double)timer.ElapsedMilliseconds;
|
||||
string result = string.Format("{0:F0}", ms);
|
||||
|
||||
await httpContext.Response.WriteAsync(
|
||||
"<script nonce=\"" + httpContext.Items[Constants.NONCE_KEY] + "\">" +
|
||||
"var pageGenerationTime = '" + result + "';" +
|
||||
"pageloadStopTimer();" +
|
||||
"</script >");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class PerformanceMonitorMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UsePerformanceMonitor(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<PerformanceMonitorMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
53
IdentityServer/Middleware/SecurityHeadersMiddleware.cs
Normal file
53
IdentityServer/Middleware/SecurityHeadersMiddleware.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
public class SecurityHeadersMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public SecurityHeadersMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public Task Invoke(HttpContext httpContext, Config config)
|
||||
{
|
||||
IHeaderDictionary headers = httpContext.Response.Headers;
|
||||
|
||||
// Access Control
|
||||
headers.Append("Access-Control-Allow-Credentials", "true");
|
||||
headers.Append("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
|
||||
headers.Append("Access-Control-Allow-Headers", "Authorization, Accept, Origin, Content-Type, X-Requested-With, Connection, Transfer-Encoding");
|
||||
|
||||
// HSTS
|
||||
headers.Append("strict-transport-security", "max-age=31536000; includeSubdomains; preload");
|
||||
|
||||
// XSS Protection
|
||||
headers.Append("X-XSS-Protection", "1; mode=block");
|
||||
|
||||
// Content Type Options
|
||||
headers.Append("X-Content-Type-Options", "nosniff");
|
||||
|
||||
// Referrer Policy
|
||||
headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin");
|
||||
|
||||
return _next(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class SecurityHeadersMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<SecurityHeadersMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
40
IdentityServer/Middleware/SetupHttpContextMiddleware.cs
Normal file
40
IdentityServer/Middleware/SetupHttpContextMiddleware.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Middleware
|
||||
{
|
||||
public class SetupHttpContextMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public SetupHttpContextMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
// Generate the NONCE used for this request
|
||||
string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringHelper.RandomString(24)));
|
||||
httpContext.Items[Constants.NONCE_KEY] = nonce;
|
||||
|
||||
await _next(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class SetupHttpContextMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseHttpContextSetup(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<SetupHttpContextMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
74
IdentityServer/Models/ApplicationUser.cs
Normal file
74
IdentityServer/Models/ApplicationUser.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Models
|
||||
{
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
public DateTime CreationDate { get; set; }
|
||||
|
||||
public DateTime LastSeen { get; set; }
|
||||
|
||||
public AccountType AccountType { get; set; }
|
||||
|
||||
public AccountStatus AccountStatus { get; set; }
|
||||
|
||||
public string PGPPublicKey { get; set; }
|
||||
|
||||
public ApplicationUser() : base()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
public ApplicationUser(string userName) : base(userName)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
CreationDate = DateTime.Now;
|
||||
LastSeen = DateTime.Now;
|
||||
AccountType = AccountType.Basic;
|
||||
AccountStatus = AccountStatus.Active;
|
||||
PGPPublicKey = null;
|
||||
}
|
||||
|
||||
public List<Claim> ToClaims()
|
||||
{
|
||||
var claims = new List<Claim>();
|
||||
claims.Add(new Claim("username", UserName));
|
||||
claims.Add(new Claim("creation-date", CreationDate.ToString("o")));
|
||||
claims.Add(new Claim("last-seen", LastSeen.ToString("o")));
|
||||
claims.Add(new Claim("account-type", AccountType.ToString()));
|
||||
claims.Add(new Claim("account-status", AccountStatus.ToString()));
|
||||
claims.Add(new Claim("recovery-email", Email ?? string.Empty));
|
||||
claims.Add(new Claim("recovery-verified", EmailConfirmed.ToString()));
|
||||
claims.Add(new Claim("2fa-enabled", TwoFactorEnabled.ToString()));
|
||||
claims.Add(new Claim("pgp-public-key", PGPPublicKey ?? string.Empty));
|
||||
return claims;
|
||||
}
|
||||
|
||||
public JObject ToJson()
|
||||
{
|
||||
return new JObject()
|
||||
{
|
||||
new JProperty("username", UserName),
|
||||
new JProperty("creation-date", CreationDate),
|
||||
new JProperty("last-seen", LastSeen),
|
||||
new JProperty("account-type", AccountType),
|
||||
new JProperty("account-status", AccountStatus),
|
||||
new JProperty("recovery-email", Email),
|
||||
new JProperty("recovery-verified", EmailConfirmed),
|
||||
new JProperty("2fa-enabled", TwoFactorEnabled),
|
||||
new JProperty("pgp-public-key", PGPPublicKey)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
12
IdentityServer/Models/ConsentInputModel.cs
Normal file
12
IdentityServer/Models/ConsentInputModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Teknik.IdentityServer.Models
|
||||
{
|
||||
public class ConsentInputModel
|
||||
{
|
||||
public string Button { get; set; }
|
||||
public IEnumerable<string> ScopesConsented { get; set; }
|
||||
public bool RememberConsent { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
}
|
||||
}
|
14
IdentityServer/Models/LoginInputModel.cs
Normal file
14
IdentityServer/Models/LoginInputModel.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Teknik.IdentityServer.Models
|
||||
{
|
||||
public class LoginInputModel
|
||||
{
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
public bool RememberMe { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
}
|
||||
}
|
8
IdentityServer/Models/LogoutInputModel.cs
Normal file
8
IdentityServer/Models/LogoutInputModel.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Teknik.IdentityServer.Models
|
||||
{
|
||||
public class LogoutInputModel
|
||||
{
|
||||
public string LogoutId { get; set; }
|
||||
public string ReturnURL { get; set; }
|
||||
}
|
||||
}
|
13
IdentityServer/Models/Manage/CheckPasswordModel.cs
Normal file
13
IdentityServer/Models/Manage/CheckPasswordModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class CheckPasswordModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
17
IdentityServer/Models/Manage/CreateClientModel.cs
Normal file
17
IdentityServer/Models/Manage/CreateClientModel.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class CreateClientModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string HomepageUrl { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
public ICollection<string> AllowedScopes { get; set; }
|
||||
}
|
||||
}
|
12
IdentityServer/Models/Manage/DeleteClientModel.cs
Normal file
12
IdentityServer/Models/Manage/DeleteClientModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class DeleteClientModel
|
||||
{
|
||||
public string ClientId { get; set; }
|
||||
}
|
||||
}
|
12
IdentityServer/Models/Manage/DeleteUserModel.cs
Normal file
12
IdentityServer/Models/Manage/DeleteUserModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class DeleteUserModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
12
IdentityServer/Models/Manage/Disable2FAModel.cs
Normal file
12
IdentityServer/Models/Manage/Disable2FAModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class Disable2FAModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
17
IdentityServer/Models/Manage/EditClientModel.cs
Normal file
17
IdentityServer/Models/Manage/EditClientModel.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class EditClientModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string HomepageUrl { get; set; }
|
||||
public string LogoUrl { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
}
|
||||
}
|
13
IdentityServer/Models/Manage/Enable2FAModel.cs
Normal file
13
IdentityServer/Models/Manage/Enable2FAModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class Enable2FAModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Code { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class GeneratePasswordResetTokenModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
12
IdentityServer/Models/Manage/GenerateRecoveryCodesModel.cs
Normal file
12
IdentityServer/Models/Manage/GenerateRecoveryCodesModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class GenerateRecoveryCodesModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
13
IdentityServer/Models/Manage/GetClientModel.cs
Normal file
13
IdentityServer/Models/Manage/GetClientModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class GetClientModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string ClientID { get; set; }
|
||||
}
|
||||
}
|
12
IdentityServer/Models/Manage/GetClientsModel.cs
Normal file
12
IdentityServer/Models/Manage/GetClientsModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class GetClientsModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
25
IdentityServer/Models/Manage/NewUserModel.cs
Normal file
25
IdentityServer/Models/Manage/NewUserModel.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class NewUserModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public AccountType AccountType { get; set; }
|
||||
public AccountStatus AccountStatus { get; set; }
|
||||
public string RecoveryEmail { get; set; }
|
||||
public bool RecoveryVerified { get; set; }
|
||||
public string PGPPublicKey { get; set; }
|
||||
|
||||
public NewUserModel()
|
||||
{
|
||||
AccountType = AccountType.Basic;
|
||||
AccountStatus = AccountStatus.Active;
|
||||
}
|
||||
}
|
||||
}
|
12
IdentityServer/Models/Manage/Reset2FAKeyModel.cs
Normal file
12
IdentityServer/Models/Manage/Reset2FAKeyModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class Reset2FAKeyModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
14
IdentityServer/Models/Manage/ResetPasswordModel.cs
Normal file
14
IdentityServer/Models/Manage/ResetPasswordModel.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class ResetPasswordModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
14
IdentityServer/Models/Manage/UpdateAccountStatusModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdateAccountStatusModel.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class UpdateAccountStatusModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public AccountStatus AccountStatus { get; set; }
|
||||
}
|
||||
}
|
14
IdentityServer/Models/Manage/UpdateAccountTypeModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdateAccountTypeModel.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class UpdateAccountTypeModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public AccountType AccountType { get; set; }
|
||||
}
|
||||
}
|
13
IdentityServer/Models/Manage/UpdateEmailModel.cs
Normal file
13
IdentityServer/Models/Manage/UpdateEmailModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class UpdateEmailModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
13
IdentityServer/Models/Manage/UpdateEmailVerifiedModel.cs
Normal file
13
IdentityServer/Models/Manage/UpdateEmailVerifiedModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class UpdateEmailVerifiedModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public bool Verified { get; set; }
|
||||
}
|
||||
}
|
14
IdentityServer/Models/Manage/UpdatePGPPublicKeyModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdatePGPPublicKeyModel.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class UpdatePGPPublicKeyModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string PGPPublicKey { get; set; }
|
||||
}
|
||||
}
|
14
IdentityServer/Models/Manage/UpdatePasswordModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdatePasswordModel.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class UpdatePasswordModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string CurrentPassword { get; set; }
|
||||
public string NewPassword { get; set; }
|
||||
}
|
||||
}
|
13
IdentityServer/Models/Manage/VerifyEmailModel.cs
Normal file
13
IdentityServer/Models/Manage/VerifyEmailModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.IdentityServer.Models.Manage
|
||||
{
|
||||
public class VerifyEmailModel
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
16
IdentityServer/Models/ProcessConsentResult.cs
Normal file
16
IdentityServer/Models/ProcessConsentResult.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
|
||||
namespace Teknik.IdentityServer.Models
|
||||
{
|
||||
public class ProcessConsentResult
|
||||
{
|
||||
public bool IsRedirect => RedirectUri != null;
|
||||
public string RedirectUri { get; set; }
|
||||
|
||||
public bool ShowView => ViewModel != null;
|
||||
public ConsentViewModel ViewModel { get; set; }
|
||||
|
||||
public bool HasValidationError => ValidationError != null;
|
||||
public string ValidationError { get; set; }
|
||||
}
|
||||
}
|
16
IdentityServer/Options/AccountOptions.cs
Normal file
16
IdentityServer/Options/AccountOptions.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Teknik.IdentityServer.Options
|
||||
{
|
||||
public class AccountOptions
|
||||
{
|
||||
public static bool AllowLocalLogin = true;
|
||||
public static bool AllowRememberLogin = true;
|
||||
public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);
|
||||
|
||||
public static bool ShowLogoutPrompt = true;
|
||||
public static bool AutomaticRedirectAfterSignOut = true;
|
||||
|
||||
public static string InvalidCredentialsErrorMessage = "Invalid username or password";
|
||||
}
|
||||
}
|
12
IdentityServer/Options/ConsentOptions.cs
Normal file
12
IdentityServer/Options/ConsentOptions.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Teknik.IdentityServer.Options
|
||||
{
|
||||
public class ConsentOptions
|
||||
{
|
||||
public static bool EnableOfflineAccess = true;
|
||||
public static string OfflineAccessDisplayName = "Offline Access";
|
||||
public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline";
|
||||
|
||||
public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission";
|
||||
public static readonly string InvalidSelectionErrorMessage = "Invalid selection";
|
||||
}
|
||||
}
|
27
IdentityServer/Program.cs
Normal file
27
IdentityServer/Program.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildWebHost(args).Run();
|
||||
}
|
||||
|
||||
public static IWebHost BuildWebHost(string[] args)
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
|
||||
return WebHost.CreateDefaultBuilder(args)
|
||||
.UseConfiguration(config)
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
|
||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<WebPublishMethod>MSDeploy</WebPublishMethod>
|
||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||
<SiteUrlToLaunchAfterPublish>https://authdev.teknik.io</SiteUrlToLaunchAfterPublish>
|
||||
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>True</ExcludeApp_Data>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<ProjectGuid>05842e03-223a-4f43-9e81-d968a9475a97</ProjectGuid>
|
||||
<SelfContained>false</SelfContained>
|
||||
<_IsPortable>true</_IsPortable>
|
||||
<MSDeployServiceURL>ams1.teknik.io</MSDeployServiceURL>
|
||||
<DeployIisAppPath>TeknikIdentityDev</DeployIisAppPath>
|
||||
<RemoteSitePhysicalPath />
|
||||
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
|
||||
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
|
||||
<EnableMSDeployBackup>True</EnableMSDeployBackup>
|
||||
<UserName>Administrator</UserName>
|
||||
<_SavePWD>True</_SavePWD>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
|
||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<WebPublishMethod>MSDeploy</WebPublishMethod>
|
||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||
<SiteUrlToLaunchAfterPublish>https://auth.teknik.io/</SiteUrlToLaunchAfterPublish>
|
||||
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>True</ExcludeApp_Data>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<ProjectGuid>eb46e1eb-fdc2-4168-a0ad-8b284d44b13e</ProjectGuid>
|
||||
<SelfContained>false</SelfContained>
|
||||
<_IsPortable>true</_IsPortable>
|
||||
<MSDeployServiceURL>ams1.teknik.io</MSDeployServiceURL>
|
||||
<DeployIisAppPath>TeknikIdentity</DeployIisAppPath>
|
||||
<RemoteSitePhysicalPath />
|
||||
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
|
||||
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
|
||||
<EnableMSDeployBackup>True</EnableMSDeployBackup>
|
||||
<UserName>Administrator</UserName>
|
||||
<_SavePWD>True</_SavePWD>
|
||||
</PropertyGroup>
|
||||
</Project>
|
35
IdentityServer/Properties/launchSettings.json
Normal file
35
IdentityServer/Properties/launchSettings.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "https://localhost:44350/",
|
||||
"sslPort": 44350
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IdentityServer": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:6002/"
|
||||
},
|
||||
"IdentityServer - Prod": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Production"
|
||||
},
|
||||
"applicationUrl": "https://localhost:6002/"
|
||||
}
|
||||
}
|
||||
}
|
49
IdentityServer/Scripts/Error.js
Normal file
49
IdentityServer/Scripts/Error.js
Normal file
@ -0,0 +1,49 @@
|
||||
$(document).ready(function () {
|
||||
$('#submitErrorReport').click(function () {
|
||||
bootbox.prompt({
|
||||
title: "Please enter any additional information that could help us",
|
||||
inputType: 'textarea',
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
errorMsg = $("#errorMsg").html();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: submitErrorReportURL,
|
||||
data: AddAntiForgeryToken({
|
||||
Message: result,
|
||||
Exception: errorMsg,
|
||||
CurrentUrl: window.location.href
|
||||
}),
|
||||
success: function (response) {
|
||||
if (response.result) {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html(
|
||||
'<div class="alert alert-info alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>Thank you for your help! Feedback has been submitted.</div>');
|
||||
} else {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg")
|
||||
.html(
|
||||
'<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' +
|
||||
parseErrorMessage(response) +
|
||||
'</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".view-details-button").on("click",
|
||||
function () {
|
||||
var link = $(this);
|
||||
var linkText = link.text().toUpperCase();
|
||||
|
||||
if (linkText === "SHOW DETAILS") {
|
||||
link.text("Hide Details");
|
||||
} else {
|
||||
link.text("Show Details");
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
6
IdentityServer/Scripts/signout-redirect.js
Normal file
6
IdentityServer/Scripts/signout-redirect.js
Normal file
@ -0,0 +1,6 @@
|
||||
window.addEventListener("load", function () {
|
||||
var a = document.querySelector("a.PostLogoutRedirectUri");
|
||||
if (a) {
|
||||
window.location = a.href;
|
||||
}
|
||||
});
|
62
IdentityServer/Security/PasswordHasher.cs
Normal file
62
IdentityServer/Security/PasswordHasher.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
using Microsoft.Extensions.Identity.Core;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Teknik.Utilities.Cryptography;
|
||||
using Teknik.Utilities;
|
||||
using System.Text;
|
||||
using Teknik.IdentityServer.Models;
|
||||
|
||||
namespace Teknik.IdentityServer.Security
|
||||
{
|
||||
public class TeknikPasswordHasher : PasswordHasher<ApplicationUser>
|
||||
{
|
||||
private readonly Config _config;
|
||||
|
||||
public TeknikPasswordHasher(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
|
||||
{
|
||||
if (hashedPassword == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hashedPassword));
|
||||
}
|
||||
if (providedPassword == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providedPassword));
|
||||
}
|
||||
|
||||
// Test legacy password hashes
|
||||
#region Legacy Checks
|
||||
byte[] hashBytes = SHA384.Hash(user.UserName, providedPassword);
|
||||
string hash = hashBytes.ToHex();
|
||||
if (hashedPassword == hash)
|
||||
{
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
|
||||
hash = Encoding.ASCII.GetString(hashBytes);
|
||||
if (hashedPassword == hash)
|
||||
{
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
|
||||
hash = SHA256.Hash(providedPassword, _config.Salt1, _config.Salt2);
|
||||
if (hashedPassword == hash)
|
||||
{
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
#endregion
|
||||
|
||||
// Test Latest
|
||||
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
}
|
||||
}
|
||||
}
|
39
IdentityServer/Security/SecurityHeadersAttribute.cs
Normal file
39
IdentityServer/Security/SecurityHeadersAttribute.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace Teknik.IdentityServer.Security
|
||||
{
|
||||
public class SecurityHeadersAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
var result = context.Result;
|
||||
if (result is ViewResult)
|
||||
{
|
||||
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options"))
|
||||
{
|
||||
context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
||||
}
|
||||
if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options"))
|
||||
{
|
||||
context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
|
||||
}
|
||||
|
||||
var csp = "default-src 'self';";
|
||||
// an example if you need client images to be displayed from twitter
|
||||
//var csp = "default-src 'self'; img-src 'self' https://pbs.twimg.com";
|
||||
|
||||
// once for standards compliant browsers
|
||||
if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
|
||||
{
|
||||
context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp);
|
||||
}
|
||||
// and once again for IE
|
||||
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
|
||||
{
|
||||
context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
IdentityServer/Security/TeknikRedirectUriValidator.cs
Normal file
60
IdentityServer/Security/TeknikRedirectUriValidator.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.Configuration;
|
||||
|
||||
namespace Teknik.IdentityServer.Security
|
||||
{
|
||||
public class TeknikRedirectUriValidator : IRedirectUriValidator
|
||||
{
|
||||
private readonly Config _config;
|
||||
|
||||
public TeknikRedirectUriValidator(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public async Task<bool> IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client)
|
||||
{
|
||||
if (client.PostLogoutRedirectUris != null && client.PostLogoutRedirectUris.Any())
|
||||
{
|
||||
if (client.PostLogoutRedirectUris.Contains(requestedUri))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add special case for pre-configured redirect URIs
|
||||
if (client.ClientId == _config.UserConfig.IdentityServerConfig.ClientId &&
|
||||
_config.UserConfig.IdentityServerConfig.PostLogoutRedirectUris != null &&
|
||||
_config.UserConfig.IdentityServerConfig.PostLogoutRedirectUris.Any())
|
||||
{
|
||||
if (_config.UserConfig.IdentityServerConfig.PostLogoutRedirectUris.Contains(requestedUri))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> IsRedirectUriValidAsync(string requestedUri, Client client)
|
||||
{
|
||||
if (client.RedirectUris != null && client.RedirectUris.Any())
|
||||
{
|
||||
if (client.RedirectUris.Contains(requestedUri))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add special case for pre-configured redirect URIs
|
||||
if (client.ClientId == _config.UserConfig.IdentityServerConfig.ClientId &&
|
||||
_config.UserConfig.IdentityServerConfig.RedirectUris != null &&
|
||||
_config.UserConfig.IdentityServerConfig.RedirectUris.Any())
|
||||
{
|
||||
if (_config.UserConfig.IdentityServerConfig.RedirectUris.Contains(requestedUri))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
107
IdentityServer/Services/AccountService.cs
Normal file
107
IdentityServer/Services/AccountService.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using IdentityModel;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using Teknik.IdentityServer.Options;
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
|
||||
namespace Teknik.IdentityServer.Services
|
||||
{
|
||||
public class AccountService
|
||||
{
|
||||
private readonly IClientStore _clientStore;
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IAuthenticationSchemeProvider _schemeProvider;
|
||||
|
||||
public AccountService(
|
||||
IIdentityServerInteractionService interaction,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IAuthenticationSchemeProvider schemeProvider,
|
||||
IClientStore clientStore)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_schemeProvider = schemeProvider;
|
||||
_clientStore = clientStore;
|
||||
}
|
||||
|
||||
public async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
|
||||
{
|
||||
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||
|
||||
var allowLocal = true;
|
||||
if (context?.ClientId != null)
|
||||
{
|
||||
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
|
||||
if (client != null)
|
||||
{
|
||||
allowLocal = client.EnableLocalLogin;
|
||||
}
|
||||
}
|
||||
|
||||
return new LoginViewModel
|
||||
{
|
||||
AllowRememberLogin = AccountOptions.AllowRememberLogin,
|
||||
EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin,
|
||||
ReturnUrl = returnUrl,
|
||||
Username = context?.LoginHint
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model)
|
||||
{
|
||||
var vm = await BuildLoginViewModelAsync(model.ReturnUrl);
|
||||
vm.Username = model.Username;
|
||||
vm.RememberMe = model.RememberMe;
|
||||
return vm;
|
||||
}
|
||||
|
||||
public async Task<LogoutViewModel> BuildLogoutViewModelAsync(string logoutId)
|
||||
{
|
||||
var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt };
|
||||
|
||||
var user = _httpContextAccessor.HttpContext.User;
|
||||
if (user?.Identity.IsAuthenticated != true)
|
||||
{
|
||||
// if the user is not authenticated, then just show logged out page
|
||||
vm.ShowLogoutPrompt = false;
|
||||
return vm;
|
||||
}
|
||||
|
||||
var context = await _interaction.GetLogoutContextAsync(logoutId);
|
||||
if (context?.ShowSignoutPrompt == false)
|
||||
{
|
||||
// it's safe to automatically sign-out
|
||||
vm.ShowLogoutPrompt = false;
|
||||
return vm;
|
||||
}
|
||||
|
||||
// show the logout prompt. this prevents attacks where the user
|
||||
// is automatically signed out by another malicious web page.
|
||||
return vm;
|
||||
}
|
||||
|
||||
public async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
|
||||
{
|
||||
// get context information (client name, post logout redirect URI and iframe for federated signout)
|
||||
var logout = await _interaction.GetLogoutContextAsync(logoutId);
|
||||
|
||||
var vm = new LoggedOutViewModel
|
||||
{
|
||||
AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut,
|
||||
PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
|
||||
ClientName = logout?.ClientId,
|
||||
SignOutIframeUrl = logout?.SignOutIFrameUrl,
|
||||
LogoutId = logoutId
|
||||
};
|
||||
|
||||
return vm;
|
||||
}
|
||||
}
|
||||
}
|
190
IdentityServer/Services/ConsentService.cs
Normal file
190
IdentityServer/Services/ConsentService.cs
Normal file
@ -0,0 +1,190 @@
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using Teknik.IdentityServer.Options;
|
||||
using Teknik.IdentityServer.ViewModels;
|
||||
|
||||
namespace Teknik.IdentityServer.Services
|
||||
{
|
||||
public class ConsentService
|
||||
{
|
||||
private readonly IClientStore _clientStore;
|
||||
private readonly IResourceStore _resourceStore;
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ConsentService(
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clientStore,
|
||||
IResourceStore resourceStore,
|
||||
ILogger logger)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_clientStore = clientStore;
|
||||
_resourceStore = resourceStore;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
|
||||
{
|
||||
var result = new ProcessConsentResult();
|
||||
|
||||
ConsentResponse grantedConsent = null;
|
||||
|
||||
// user clicked 'no' - send back the standard 'access_denied' response
|
||||
if (model.Button == "no")
|
||||
{
|
||||
grantedConsent = ConsentResponse.Denied;
|
||||
}
|
||||
// user clicked 'yes' - validate the data
|
||||
else if (model.Button == "yes" && model != null)
|
||||
{
|
||||
// if the user consented to some scope, build the response model
|
||||
if (model.ScopesConsented != null && model.ScopesConsented.Any())
|
||||
{
|
||||
var scopes = model.ScopesConsented;
|
||||
if (ConsentOptions.EnableOfflineAccess == false)
|
||||
{
|
||||
scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess);
|
||||
}
|
||||
|
||||
grantedConsent = new ConsentResponse
|
||||
{
|
||||
RememberConsent = model.RememberConsent,
|
||||
ScopesConsented = scopes.ToArray()
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
|
||||
}
|
||||
|
||||
if (grantedConsent != null)
|
||||
{
|
||||
// validate return url is still valid
|
||||
var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
|
||||
if (request == null) return result;
|
||||
|
||||
// communicate outcome of consent back to identityserver
|
||||
await _interaction.GrantConsentAsync(request, grantedConsent);
|
||||
|
||||
// indicate that's it ok to redirect back to authorization endpoint
|
||||
result.RedirectUri = model.ReturnUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to redisplay the consent UI
|
||||
result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<ConsentViewModel> BuildViewModelAsync(string returnUrl, ConsentInputModel model = null)
|
||||
{
|
||||
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||
if (request != null)
|
||||
{
|
||||
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
|
||||
if (client != null)
|
||||
{
|
||||
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
|
||||
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any()))
|
||||
{
|
||||
return CreateConsentViewModel(model, returnUrl, request, client, resources);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("Invalid client id: {0}", request.ClientId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("No consent request matching request: {0}", returnUrl);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConsentViewModel CreateConsentViewModel(
|
||||
ConsentInputModel model, string returnUrl,
|
||||
AuthorizationRequest request,
|
||||
Client client, Resources resources)
|
||||
{
|
||||
var vm = new ConsentViewModel();
|
||||
vm.RememberConsent = model?.RememberConsent ?? true;
|
||||
vm.ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>();
|
||||
|
||||
vm.ReturnUrl = returnUrl;
|
||||
|
||||
vm.ClientName = client.ClientName ?? client.ClientId;
|
||||
vm.ClientUrl = client.ClientUri;
|
||||
vm.ClientLogoUrl = client.LogoUri;
|
||||
vm.AllowRememberConsent = client.AllowRememberConsent;
|
||||
|
||||
vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
|
||||
vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
|
||||
if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess)
|
||||
{
|
||||
vm.ResourceScopes = vm.ResourceScopes.Union(new ScopeViewModel[] {
|
||||
GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)
|
||||
});
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
public ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
|
||||
{
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Name = identity.Name,
|
||||
DisplayName = identity.DisplayName,
|
||||
Description = identity.Description,
|
||||
Emphasize = identity.Emphasize,
|
||||
Required = identity.Required,
|
||||
Checked = check || identity.Required
|
||||
};
|
||||
}
|
||||
|
||||
public ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
|
||||
{
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Name = scope.Name,
|
||||
DisplayName = scope.DisplayName,
|
||||
Description = scope.Description,
|
||||
Emphasize = scope.Emphasize,
|
||||
Required = scope.Required,
|
||||
Checked = check || scope.Required
|
||||
};
|
||||
}
|
||||
|
||||
private ScopeViewModel GetOfflineAccessScope(bool check)
|
||||
{
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
DisplayName = ConsentOptions.OfflineAccessDisplayName,
|
||||
Description = ConsentOptions.OfflineAccessDescription,
|
||||
Emphasize = true,
|
||||
Checked = check
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
250
IdentityServer/Startup.cs
Normal file
250
IdentityServer/Startup.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using IdentityServer4.EntityFramework.DbContexts;
|
||||
using IdentityServer4.EntityFramework.Mappers;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Teknik.Configuration;
|
||||
using Teknik.IdentityServer.Configuration;
|
||||
using Teknik.IdentityServer.Security;
|
||||
using Teknik.IdentityServer.Middleware;
|
||||
using Teknik.Logging;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using IdentityServer4.Services;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Teknik.IdentityServer
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration, IHostingEnvironment env)
|
||||
{
|
||||
Configuration = configuration;
|
||||
Environment = env;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
public IHostingEnvironment Environment { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
string dataDir = Configuration["ConfigDirectory"];
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);
|
||||
|
||||
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
|
||||
|
||||
// Create Configuration Singleton
|
||||
services.AddScoped<Config, Config>(opt => Config.Load(dataDir));
|
||||
|
||||
// Build an intermediate service provider
|
||||
var sp = services.BuildServiceProvider();
|
||||
|
||||
// Resolve the services from the service provider
|
||||
var config = sp.GetService<Config>();
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Cookie.Name = "TeknikAuth";
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||
options.Cookie.Expiration = TimeSpan.FromDays(30);
|
||||
options.ExpireTimeSpan = TimeSpan.FromDays(30);
|
||||
});
|
||||
|
||||
services.AddHttpsRedirection(options =>
|
||||
{
|
||||
options.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
|
||||
});
|
||||
|
||||
// Sessions
|
||||
services.AddResponseCaching();
|
||||
services.AddMemoryCache();
|
||||
services.AddSession();
|
||||
|
||||
// Set the anti-forgery cookie name
|
||||
services.AddAntiforgery(options =>
|
||||
{
|
||||
options.Cookie.Name = "TeknikAuthAntiForgery";
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||
});
|
||||
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||
|
||||
services.AddDbContext<ApplicationDbContext>(builder =>
|
||||
builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)));
|
||||
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||
{
|
||||
options.Password = new PasswordOptions()
|
||||
{
|
||||
RequireDigit = false,
|
||||
RequiredLength = 4,
|
||||
RequiredUniqueChars = 1,
|
||||
RequireLowercase = false,
|
||||
RequireNonAlphanumeric = false,
|
||||
RequireUppercase = false
|
||||
};
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.AddIdentityServer(options =>
|
||||
{
|
||||
options.Events.RaiseErrorEvents = true;
|
||||
options.Events.RaiseInformationEvents = true;
|
||||
options.Events.RaiseFailureEvents = true;
|
||||
options.Events.RaiseSuccessEvents = true;
|
||||
options.UserInteraction.ErrorUrl = "/Error/IdentityError";
|
||||
options.UserInteraction.ErrorIdParameter = "errorId";
|
||||
options.Cors.CorsPaths.Add(new PathString("/connect/authorize"));
|
||||
options.Cors.CorsPaths.Add(new PathString("/connect/endsession"));
|
||||
options.Cors.CorsPaths.Add(new PathString("/connect/checksession"));
|
||||
options.Cors.CorsPaths.Add(new PathString("/connect/introspect"));
|
||||
})
|
||||
.AddOperationalStore(options =>
|
||||
options.ConfigureDbContext = builder =>
|
||||
builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
|
||||
.AddConfigurationStore(options =>
|
||||
options.ConfigureDbContext = builder =>
|
||||
builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
.AddRedirectUriValidator<TeknikRedirectUriValidator>()
|
||||
.AddDeveloperSigningCredential();
|
||||
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
foreach (var policy in Policies.Get())
|
||||
{
|
||||
options.AddPolicy(policy.Name, p =>
|
||||
{
|
||||
foreach (var scope in policy.Scopes)
|
||||
{
|
||||
p.RequireScope(scope);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
services.AddAuthentication("Bearer")
|
||||
.AddIdentityServerAuthentication(options =>
|
||||
{
|
||||
options.Authority = config.UserConfig.IdentityServerConfig.Authority;
|
||||
options.RequireHttpsMetadata = true;
|
||||
|
||||
options.ApiName = "auth-api";
|
||||
});
|
||||
|
||||
services.AddTransient<IPasswordHasher<ApplicationUser>, TeknikPasswordHasher>();
|
||||
services.AddTransient<IProfileService, TeknikProfileService>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Config config)
|
||||
{
|
||||
// Initiate Logging
|
||||
loggerFactory.AddLogger(config);
|
||||
|
||||
// Setup the HttpContext
|
||||
app.UseHttpContextSetup();
|
||||
|
||||
// HttpContext Session
|
||||
app.UseSession(new SessionOptions()
|
||||
{
|
||||
IdleTimeout = TimeSpan.FromMinutes(30),
|
||||
Cookie = new CookieBuilder()
|
||||
{
|
||||
Domain = null,
|
||||
Name = "TeknikAuthSession",
|
||||
SecurePolicy = CookieSecurePolicy.Always,
|
||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict
|
||||
}
|
||||
});
|
||||
|
||||
// Use Exception Handling
|
||||
app.UseErrorHandler(config);
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
// Custom Middleware
|
||||
app.UseBlacklist();
|
||||
app.UseCORS();
|
||||
app.UseCSP();
|
||||
app.UseSecurityHeaders();
|
||||
|
||||
// Cache Responses
|
||||
app.UseResponseCaching();
|
||||
|
||||
// Force a HTTPS redirection (301)
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
// Setup static files anc cache them client side
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
OnPrepareResponse = ctx =>
|
||||
{
|
||||
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
|
||||
}
|
||||
});
|
||||
|
||||
InitializeDbTestDataAsync(app, config).Wait();
|
||||
|
||||
app.UseIdentityServer();
|
||||
|
||||
app.UseMvcWithDefaultRoute();
|
||||
}
|
||||
|
||||
private static async System.Threading.Tasks.Task InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
|
||||
{
|
||||
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
|
||||
scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>().Database.Migrate();
|
||||
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
|
||||
|
||||
var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
|
||||
|
||||
if (!context.Clients.Any())
|
||||
{
|
||||
foreach (var client in Clients.Get(config))
|
||||
{
|
||||
context.Clients.Add(client.ToEntity());
|
||||
}
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
if (!context.IdentityResources.Any())
|
||||
{
|
||||
foreach (var resource in Resources.GetIdentityResources())
|
||||
{
|
||||
context.IdentityResources.Add(resource.ToEntity());
|
||||
}
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
if (!context.ApiResources.Any())
|
||||
{
|
||||
foreach (var resource in Resources.GetApiResources(config))
|
||||
{
|
||||
context.ApiResources.Add(resource.ToEntity());
|
||||
}
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
IdentityServer/TeknikProfileService.cs
Normal file
49
IdentityServer/TeknikProfileService.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using IdentityModel;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Teknik.IdentityServer.Models;
|
||||
using Teknik.Utilities;
|
||||
|
||||
namespace Teknik.IdentityServer
|
||||
{
|
||||
public class TeknikProfileService : IProfileService
|
||||
{
|
||||
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public TeknikProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_claimsFactory = claimsFactory;
|
||||
}
|
||||
|
||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||
{
|
||||
var sub = context.Subject.GetSubjectId();
|
||||
var user = await _userManager.FindByIdAsync(sub);
|
||||
var principal = await _claimsFactory.CreateAsync(user);
|
||||
|
||||
var claims = principal.Claims.ToList();
|
||||
|
||||
claims.AddRange(user.ToClaims());
|
||||
|
||||
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
|
||||
|
||||
context.IssuedClaims = claims;
|
||||
}
|
||||
|
||||
public async Task IsActiveAsync(IsActiveContext context)
|
||||
{
|
||||
var sub = context.Subject.GetSubjectId();
|
||||
var user = await _userManager.FindByIdAsync(sub);
|
||||
context.IsActive = user != null && user.AccountStatus == Utilities.AccountStatus.Active;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user