Browse Source

Merged core into master

tags/3.0.0
Teknikode 7 months ago
parent
commit
09630a6714
100 changed files with 7202 additions and 236 deletions
  1. 4
    6
      .editorconfig
  2. 111
    49
      .gitignore
  3. 1
    7
      Configuration/ApiConfig.cs
  4. 1
    6
      Configuration/BlogConfig.cs
  5. 77
    79
      Configuration/Config.cs
  6. 19
    0
      Configuration/Configuration.csproj
  7. 1
    8
      Configuration/ContactConfig.cs
  8. 0
    0
      Configuration/DatabaseConfig.cs
  9. 1
    7
      Configuration/EmailAccount.cs
  10. 1
    6
      Configuration/EmailConfig.cs
  11. 1
    7
      Configuration/GitConfig.cs
  12. 1
    7
      Configuration/IRCConfig.cs
  13. 32
    0
      Configuration/IdentityServerConfig.cs
  14. 1
    6
      Configuration/LoggingConfig.cs
  15. 33
    0
      Configuration/PasteConfig.cs
  16. 1
    7
      Configuration/PiwikConfig.cs
  17. 1
    5
      Configuration/PodcastConfig.cs
  18. 1
    6
      Configuration/ShortenerConfig.cs
  19. 1
    7
      Configuration/StatsConfig.cs
  20. 1
    5
      Configuration/StreamConfig.cs
  21. 0
    3
      Configuration/UploadConfig.cs
  22. 2
    7
      Configuration/UserConfig.cs
  23. 1
    6
      Configuration/VaultConfig.cs
  24. 15
    0
      GitService/GitService.csproj
  25. 148
    0
      GitService/GiteaService.cs
  26. 23
    0
      GitService/IGitService.cs
  27. 173
    0
      GitService/MysqlDatabase.cs
  28. 0
    2
      GitVersionConfig.yaml
  29. 52
    0
      IdentityServer.sln
  30. 11
    0
      IdentityServer/ApplicationDbContext.cs
  31. 143
    0
      IdentityServer/Configuration.cs
  32. 140
    0
      IdentityServer/Content/common.css
  33. 324
    0
      IdentityServer/Controllers/AccountController.cs
  34. 77
    0
      IdentityServer/Controllers/ConsentController.cs
  35. 64
    0
      IdentityServer/Controllers/DefaultController.cs
  36. 237
    0
      IdentityServer/Controllers/ErrorController.cs
  37. 93
    0
      IdentityServer/Controllers/GrantsController.cs
  38. 29
    0
      IdentityServer/Controllers/HomeController.cs
  39. 611
    0
      IdentityServer/Controllers/ManageController.cs
  40. 242
    0
      IdentityServer/Data/Migrations/ApplicationDb/20181015060219_InitialApplicationDbContextMigration.Designer.cs
  41. 225
    0
      IdentityServer/Data/Migrations/ApplicationDb/20181015060219_InitialApplicationDbContextMigration.cs
  42. 240
    0
      IdentityServer/Data/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs
  43. 679
    0
      IdentityServer/Data/Migrations/IdentityServer/ConfigurationDb/20180930040544_InitialIdentityServerConfigurationDbMigration.Designer.cs
  44. 602
    0
      IdentityServer/Data/Migrations/IdentityServer/ConfigurationDb/20180930040544_InitialIdentityServerConfigurationDbMigration.cs
  45. 677
    0
      IdentityServer/Data/Migrations/IdentityServer/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs
  46. 57
    0
      IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/20180930040529_InitialIdentityServerPersistedGrantDbMigration.Designer.cs
  47. 39
    0
      IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/20180930040529_InitialIdentityServerPersistedGrantDbMigration.cs
  48. 55
    0
      IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs
  49. 54
    0
      IdentityServer/IdentityServer.csproj
  50. BIN
      IdentityServer/Images/favicon.ico
  51. 23
    0
      IdentityServer/Images/logo-black.svg
  52. 23
    0
      IdentityServer/Images/logo-blue.svg
  53. 118
    0
      IdentityServer/Middleware/BlacklistMiddleware.cs
  54. 72
    0
      IdentityServer/Middleware/CORSMiddleware.cs
  55. 76
    0
      IdentityServer/Middleware/CSPMiddleware.cs
  56. 105
    0
      IdentityServer/Middleware/ErrorHandlerMiddleware.cs
  57. 68
    0
      IdentityServer/Middleware/PerformanceMonitorMiddleware.cs
  58. 53
    0
      IdentityServer/Middleware/SecurityHeadersMiddleware.cs
  59. 40
    0
      IdentityServer/Middleware/SetupHttpContextMiddleware.cs
  60. 74
    0
      IdentityServer/Models/ApplicationUser.cs
  61. 12
    0
      IdentityServer/Models/ConsentInputModel.cs
  62. 14
    0
      IdentityServer/Models/LoginInputModel.cs
  63. 8
    0
      IdentityServer/Models/LogoutInputModel.cs
  64. 13
    0
      IdentityServer/Models/Manage/CheckPasswordModel.cs
  65. 17
    0
      IdentityServer/Models/Manage/CreateClientModel.cs
  66. 12
    0
      IdentityServer/Models/Manage/DeleteClientModel.cs
  67. 12
    0
      IdentityServer/Models/Manage/DeleteUserModel.cs
  68. 12
    0
      IdentityServer/Models/Manage/Disable2FAModel.cs
  69. 17
    0
      IdentityServer/Models/Manage/EditClientModel.cs
  70. 13
    0
      IdentityServer/Models/Manage/Enable2FAModel.cs
  71. 12
    0
      IdentityServer/Models/Manage/GeneratePasswordResetTokenModel.cs
  72. 12
    0
      IdentityServer/Models/Manage/GenerateRecoveryCodesModel.cs
  73. 13
    0
      IdentityServer/Models/Manage/GetClientModel.cs
  74. 12
    0
      IdentityServer/Models/Manage/GetClientsModel.cs
  75. 25
    0
      IdentityServer/Models/Manage/NewUserModel.cs
  76. 12
    0
      IdentityServer/Models/Manage/Reset2FAKeyModel.cs
  77. 14
    0
      IdentityServer/Models/Manage/ResetPasswordModel.cs
  78. 14
    0
      IdentityServer/Models/Manage/UpdateAccountStatusModel.cs
  79. 14
    0
      IdentityServer/Models/Manage/UpdateAccountTypeModel.cs
  80. 13
    0
      IdentityServer/Models/Manage/UpdateEmailModel.cs
  81. 13
    0
      IdentityServer/Models/Manage/UpdateEmailVerifiedModel.cs
  82. 14
    0
      IdentityServer/Models/Manage/UpdatePGPPublicKeyModel.cs
  83. 14
    0
      IdentityServer/Models/Manage/UpdatePasswordModel.cs
  84. 13
    0
      IdentityServer/Models/Manage/VerifyEmailModel.cs
  85. 16
    0
      IdentityServer/Models/ProcessConsentResult.cs
  86. 16
    0
      IdentityServer/Options/AccountOptions.cs
  87. 12
    0
      IdentityServer/Options/ConsentOptions.cs
  88. 27
    0
      IdentityServer/Program.cs
  89. 27
    0
      IdentityServer/Properties/PublishProfiles/Teknik Identity - Development.pubxml
  90. 27
    0
      IdentityServer/Properties/PublishProfiles/Teknik Identity - Production.pubxml
  91. 35
    0
      IdentityServer/Properties/launchSettings.json
  92. 49
    0
      IdentityServer/Scripts/Error.js
  93. 6
    0
      IdentityServer/Scripts/signout-redirect.js
  94. 62
    0
      IdentityServer/Security/PasswordHasher.cs
  95. 39
    0
      IdentityServer/Security/SecurityHeadersAttribute.cs
  96. 60
    0
      IdentityServer/Security/TeknikRedirectUriValidator.cs
  97. 107
    0
      IdentityServer/Services/AccountService.cs
  98. 190
    0
      IdentityServer/Services/ConsentService.cs
  99. 250
    0
      IdentityServer/Startup.cs
  100. 0
    0
      IdentityServer/TeknikProfileService.cs

+ 4
- 6
.editorconfig 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

+ 111
- 49
.gitignore 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

# NuGet Packages Directory
packages/*
## TODO: If the tool you use requires repositories.config
## uncomment the next line
#!packages/repositories.config

# 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/

# Windows Azure Build Output
*.publishproj

# 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/

# 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

# 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

Utilities/Configuration/ApiConfig.cs → Configuration/ApiConfig.cs 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
{

Utilities/Configuration/BlogConfig.cs → Configuration/BlogConfig.cs 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
{

Utilities/Configuration/Config.cs → Configuration/Config.cs 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")))
string newHash = string.Empty;
string fullPath = Path.Combine(path, _ConfigFileName);

if (!File.Exists(fullPath))
{
Save(Path.Combine(path, "Config.json"), config);
Config config = new Config();
Save(fullPath, config);
}
else

newHash = MD5.FileHash(fullPath);
if (_Config == null || _FileHash == null || newHash != _FileHash)
{
string configContents = File.ReadAllText(Path.Combine(path, "Config.json"));
config = Deserialize(configContents);
string configContents = File.ReadAllText(fullPath);
_Config = Deserialize(configContents);
_FileHash = newHash;
}
return config;
}

public static void Save(Config config)
{
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
Save(Path.Combine(path, "Config.json"), config);
return _Config;
}

public static void Save(string path, Config config)

+ 19
- 0
Configuration/Configuration.csproj 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>

Utilities/Configuration/ContactConfig.cs → Configuration/ContactConfig.cs 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
{

Utilities/Configuration/DatabaseConfig.cs → Configuration/DatabaseConfig.cs View File


Utilities/Configuration/EmailAccount.cs → Configuration/EmailAccount.cs 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
{

Utilities/Configuration/EmailConfig.cs → Configuration/EmailConfig.cs 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
{

Utilities/Configuration/GitConfig.cs → Configuration/GitConfig.cs 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
{

Utilities/Configuration/IRCConfig.cs → Configuration/IRCConfig.cs 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
{

+ 32
- 0
Configuration/IdentityServerConfig.cs 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";
}
}
}

Utilities/Configuration/LoggingConfig.cs → Configuration/LoggingConfig.cs 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
{

+ 33
- 0
Configuration/PasteConfig.cs 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";
}
}
}

Utilities/Configuration/PiwikConfig.cs → Configuration/PiwikConfig.cs 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
{

Utilities/Configuration/PodcastConfig.cs → Configuration/PodcastConfig.cs 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
{

Utilities/Configuration/ShortenerConfig.cs → Configuration/ShortenerConfig.cs 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
{

Utilities/Configuration/StatsConfig.cs → Configuration/StatsConfig.cs 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
{

Utilities/Configuration/StreamConfig.cs → Configuration/StreamConfig.cs 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
{

Utilities/Configuration/UploadConfig.cs → Configuration/UploadConfig.cs View File

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

namespace Teknik.Configuration
{

Utilities/Configuration/UserConfig.cs → Configuration/UserConfig.cs 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();
}
}
}

Utilities/Configuration/VaultConfig.cs → Configuration/VaultConfig.cs 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
{

+ 15
- 0
GitService/GitService.csproj 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
- 0
GitService/GiteaService.cs 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
- 0
GitService/IGitService.cs 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
- 0
GitService/MysqlDatabase.cs 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;
}
}
}

+ 0
- 2
GitVersionConfig.yaml View File

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

+ 52
- 0
IdentityServer.sln 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

+ 11
- 0
IdentityServer/ApplicationDbContext.cs 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) { }
}
}

+ 143
- 0
IdentityServer/Configuration.cs 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>();
}
}
}

+ 140
- 0
IdentityServer/Content/common.css 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;
}

+ 324
- 0
IdentityServer/Controllers/AccountController.cs 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");
}
}
}
}

+ 77
- 0
IdentityServer/Controllers/ConsentController.cs 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.");
}
}
}

+ 64
- 0
IdentityServer/Controllers/DefaultController.cs 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;
}
}
}

+ 237
- 0
IdentityServer/Controllers/ErrorController.cs 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);
}