1
0
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:
Uncled1023 2019-01-27 22:52:17 -08:00
commit 09630a6714
816 changed files with 39966 additions and 104216 deletions

View File

@ -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
View File

@ -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

View File

@ -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
{

View File

@ -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
{

View File

@ -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)

View 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>

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View 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";
}
}
}

View File

@ -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
{

View 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";
}
}
}

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
namespace Teknik.Configuration
{

View File

@ -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();
}
}
}

View File

@ -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
{

View 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
View 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
View 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
View 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;
}
}
}

View File

@ -1,2 +0,0 @@
assembly-versioning-scheme: MajorMinorPatch
next-version: 2.0.6

52
IdentityServer.sln Normal file
View 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

View 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) { }
}
}

View 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>();
}
}
}

View 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;
}

View 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");
}
}
}
}

View 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.");
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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
};
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View File

@ -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
}
}
}

View File

@ -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");
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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");
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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");
}
}
}

View File

@ -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
}
}
}

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View 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

View 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

View 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>();
}
}
}

View 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>();
}
}
}

View 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>();
}
}
}

View 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());
}
}
}

View 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>();
}
}
}

View 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>();
}
}
}

View 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>();
}
}
}

View 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)
};
}
}
}

View 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; }
}
}

View 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; }
}
}

View File

@ -0,0 +1,8 @@
namespace Teknik.IdentityServer.Models
{
public class LogoutInputModel
{
public string LogoutId { get; set; }
public string ReturnURL { get; set; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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 GeneratePasswordResetTokenModel
{
public string Username { get; set; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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;
}
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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; }
}
}

View 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";
}
}

View 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
View 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();
}
}
}

View File

@ -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>

View File

@ -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>

View 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/"
}
}
}

View 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">&times;</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">&times;</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");
};
}
);
});

View File

@ -0,0 +1,6 @@
window.addEventListener("load", function () {
var a = document.querySelector("a.PostLogoutRedirectUri");
if (a) {
window.location = a.href;
}
});

View 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);
}
}
}

View 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);
}
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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
View 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();
}
}
}
}
}

View 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