├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .node-version ├── DeployClient.Tests ├── DeployClient.Tests.csproj ├── PackageCrawlerTests.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── DeployClient ├── API.cs ├── App.config ├── CommandlineOptions.cs ├── DeployClient.csproj ├── PackageCrawler.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── gulpfile.js ├── package-lock.json ├── package.json ├── packages.config └── project.config.js ├── Encryption ├── Crypto.cs ├── CryptoUtilities.cs ├── Encryption.csproj └── Properties │ └── AssemblyInfo.cs ├── EncryptionTests ├── EncryptionTests.cs ├── EncryptionTests.csproj ├── Properties │ └── AssemblyInfo.cs ├── RoundTripTests.cs ├── TestUtilities.cs └── packages.config ├── LICENSE ├── PolyDeploy.DeployClient.Tests ├── AssertionExtensions.cs ├── DeployInputTests.cs ├── DeployerTests.cs ├── EncryptorTests.cs ├── InstallerTests.cs ├── PackageFileSourceTests.cs ├── PolyDeploy.DeployClient.Tests.csproj ├── RendererTests.cs ├── TestHelpers.cs ├── TestStopwatch.cs └── packages.lock.json ├── PolyDeploy.DeployClient ├── Delayer.cs ├── DeployCommand.cs ├── DeployInput.cs ├── Deployer.cs ├── Encryptor.cs ├── IDelayer.cs ├── IDeployer.cs ├── IEncryptor.cs ├── IInstaller.cs ├── IPackageFileSource.cs ├── IRenderer.cs ├── IStopwatch.cs ├── Installer.cs ├── InstallerException.cs ├── LogLevel.cs ├── PackageFileSource.cs ├── PolyDeploy.DeployClient.csproj ├── Program.cs ├── Renderer.cs ├── Session.cs ├── Stopwatch.cs └── packages.lock.json ├── PolyDeploy.Encryption ├── Crypto.cs ├── CryptoUtilities.cs ├── PolyDeploy.Encryption.csproj └── packages.lock.json ├── PolyDeploy.lutconfig ├── PolyDeploy.sln ├── PolyDeploy.sln.DotSettings ├── PolyDeploy ├── .babelrc ├── Clients │ ├── Core │ │ └── src │ │ │ ├── css │ │ │ └── main.less │ │ │ └── js │ │ │ ├── app.js │ │ │ ├── bootstrap.js │ │ │ ├── controllers │ │ │ ├── WarningController.js │ │ │ └── index.js │ │ │ └── services │ │ │ ├── APIUserDataService.js │ │ │ ├── DnnService.js │ │ │ ├── EventLogDataService.js │ │ │ ├── IPSpecDataService.js │ │ │ ├── SessionDataService.js │ │ │ ├── SettingDataService.js │ │ │ ├── WarnService.js │ │ │ └── index.js │ ├── Deploy │ │ ├── App_LocalResources │ │ │ └── Deploy.ascx.resx │ │ ├── Deploy.ascx │ │ ├── Deploy.ascx.cs │ │ ├── Deploy.ascx.designer.cs │ │ └── src │ │ │ ├── css │ │ │ └── main.less │ │ │ └── js │ │ │ ├── app.js │ │ │ ├── bootstrap.js │ │ │ ├── config.js │ │ │ ├── controllers │ │ │ ├── ResultController.js │ │ │ ├── SummaryController.js │ │ │ ├── UploadController.js │ │ │ └── index.js │ │ │ ├── directives │ │ │ └── index.js │ │ │ ├── services │ │ │ ├── SessionService.js │ │ │ └── index.js │ │ │ └── templates │ │ │ ├── install.html │ │ │ ├── result.html │ │ │ ├── summary.html │ │ │ └── upload.html │ └── Manage │ │ ├── Manage.ascx │ │ ├── Manage.ascx.cs │ │ ├── Manage.ascx.designer.cs │ │ └── src │ │ ├── css │ │ └── main.less │ │ └── js │ │ ├── app.js │ │ ├── bootstrap.js │ │ ├── config.js │ │ ├── controllers │ │ ├── EventsController.js │ │ ├── UsersController.js │ │ ├── WhitelistController.js │ │ └── index.js │ │ ├── services │ │ └── index.js │ │ └── templates │ │ ├── events.html │ │ ├── menu.html │ │ ├── users.html │ │ └── whitelist.html ├── Components │ ├── APIUserManager.cs │ ├── DataAccess │ │ ├── DataControllers │ │ │ ├── APIUserDataController.cs │ │ │ ├── EventLogDataController.cs │ │ │ ├── IPSpecDataController.cs │ │ │ ├── SessionDataController.cs │ │ │ └── SettingDataController.cs │ │ └── Models │ │ │ ├── APIUser.cs │ │ │ ├── EventLog.cs │ │ │ ├── IPSpec.cs │ │ │ ├── Obfuscated.cs │ │ │ ├── Session.cs │ │ │ └── Setting.cs │ ├── Deployment.cs │ ├── Exceptions │ │ ├── IPSpecExistsException.cs │ │ └── SettingNotFoundException.cs │ ├── FeatureController.cs │ ├── IPSpecManager.cs │ ├── InstallJob.cs │ ├── Logging │ │ ├── EventLogManager.cs │ │ └── EventLogSeverity.cs │ ├── PackageDependency.cs │ ├── PackageJob.cs │ ├── PolyDeployModuleBase.cs │ ├── PolyDeploySettingsBase.cs │ ├── RemoteDeployment.cs │ ├── SessionManager.cs │ ├── SettingManager.cs │ ├── Utilities.cs │ └── WebAPI │ │ ├── APIUserController.cs │ │ ├── ActionFilters │ │ ├── APIAuthentication.cs │ │ └── InWhitelist.cs │ │ ├── EventLogController.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── IPSpecController.cs │ │ ├── RemoteController.cs │ │ ├── Routes.cs │ │ ├── SessionController.cs │ │ └── SettingController.cs ├── Images │ └── polydeploy-logo-icon.png ├── License.txt ├── PolyDeploy.csproj ├── PolyDeploy.dnn ├── Properties │ └── AssemblyInfo.cs ├── ReleaseNotes.txt ├── SqlDataProviders │ ├── 00.01.00.SqlDataProvider │ ├── 00.02.00.SqlDataProvider │ ├── 00.03.00.SqlDataProvider │ ├── 00.04.00.SqlDataProvider │ ├── 00.06.00.SqlDataProvider │ ├── 00.07.00.SqlDataProvider │ ├── 00.09.00.SqlDataProvider │ ├── 00.09.01.SqlDataProvider │ ├── 00.09.02.SqlDataProvider │ ├── 00.09.03.SqlDataProvider │ └── Uninstall.SqlDataProvider ├── gulpfile.js ├── module.css ├── package-lock.json ├── package.json ├── packages.config ├── project.config.js └── webpack.base.js ├── README.md ├── _config.yml └── docs ├── _config.yml ├── deploy-client.md ├── docgen-menu.md ├── img └── polydeploy-logo-full.png ├── index.md └── poly-deploy ├── api-reference.md ├── api-users.md ├── installation.md ├── ip-whitelist.md └── quick-start.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI (Build and Package) 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [master, develop] 7 | pull_request: 8 | branches: [master, develop] 9 | release: 10 | types: [created] 11 | 12 | defaults: 13 | run: 14 | shell: pwsh 15 | 16 | env: 17 | VERSION_SUFFIX: alpha-${{ github.run_number }} 18 | 19 | jobs: 20 | build: 21 | strategy: 22 | matrix: 23 | configuration: [Release] 24 | 25 | runs-on: windows-latest 26 | 27 | env: 28 | NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages 29 | 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3.1.0 33 | 34 | - name: Setup MSBuild.exe 35 | uses: microsoft/setup-msbuild@v1.1.3 36 | 37 | - name: Setup .NET SDK 38 | uses: actions/setup-dotnet@v3.0.3 39 | with: 40 | dotnet-version: "7.0.x" 41 | 42 | - name: Setup NuGet.exe for use with actions 43 | uses: NuGet/setup-nuget@v1.1.1 44 | with: 45 | nuget-version: latest 46 | 47 | - name: Setup Node.js 48 | uses: actions/setup-node@v3.5.1 49 | with: 50 | node-version-file: ".node-version" 51 | cache: "npm" 52 | cache-dependency-path: | 53 | DeployClient/package-lock.json 54 | PolyDeploy/package-lock.json 55 | 56 | - name: Install npm modules 57 | run: | 58 | cd DeployClient 59 | npm ci 60 | cd ../PolyDeploy 61 | npm ci 62 | cd .. 63 | 64 | - uses: actions/cache@v3 65 | with: 66 | path: ${{ env.NUGET_PACKAGES }} 67 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} 68 | restore-keys: | 69 | ${{ runner.os }}-nuget- 70 | 71 | - name: Restore NuGet packages 72 | run: nuget restore -LockedMode 73 | 74 | - name: Build and package the application 75 | run: msbuild PolyDeploy.sln /m /p:Configuration=$env:Configuration /p:ContinuousIntegrationBuild=true /p:VersionSuffix=${{ env.VERSION_SUFFIX }} 76 | env: 77 | Configuration: ${{ matrix.configuration }} 78 | 79 | - name: Test 80 | run: dotnet test PolyDeploy.sln --configuration ${{ matrix.configuration }} --no-build 81 | 82 | - name: Publish Deploy Client 83 | run: dotnet publish PolyDeploy.DeployClient/PolyDeploy.DeployClient.csproj --configuration ${{ matrix.configuration }} --no-build --output dist/ 84 | 85 | - name: Pack Deploy Client (alpha) 86 | if: github.event_name != 'release' || github.event.action != 'created' 87 | run: dotnet pack PolyDeploy.DeployClient/PolyDeploy.DeployClient.csproj --configuration ${{ matrix.configuration }} --no-build --output dist/ --version-suffix ${{ env.VERSION_SUFFIX }} 88 | 89 | - name: Pack Deploy Client (stable) 90 | if: github.event_name == 'release' && github.event.action == 'created' 91 | run: dotnet pack PolyDeploy.DeployClient/PolyDeploy.DeployClient.csproj --configuration ${{ matrix.configuration }} --no-build --output dist/ 92 | 93 | - name: Upload build artifacts 94 | uses: actions/upload-artifact@v3.1.1 95 | with: 96 | path: dist/ 97 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 16 2 | -------------------------------------------------------------------------------- /DeployClient.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("DeployClient.Tests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("DeployClient.Tests")] 10 | [assembly: AssemblyCopyright("Copyright © 2020")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("2f81ebf7-a395-4716-8b1c-23fe9ee8b7cd")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /DeployClient.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DeployClient/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /DeployClient/CommandlineOptions.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using CommandLine.Text; 3 | 4 | namespace DeployClient 5 | { 6 | internal class CommandLineOptions 7 | { 8 | [Option('s', 9 | "silent", 10 | DefaultValue = false, 11 | HelpText = "Whether to not print any output")] 12 | public bool IsSilent { get; set; } 13 | 14 | [Option('n', 15 | "no-prompt", 16 | DefaultValue = false, 17 | HelpText = "Whether to prompt the user for confirmation")] 18 | public bool NoPrompt { get; set; } 19 | 20 | [Option('u', 21 | "target-uri", 22 | Required = false, 23 | HelpText = "The URL to deploy the packages to.", 24 | DefaultValue = null)] 25 | public string TargetUri { get; set; } 26 | 27 | [Option('a', 28 | "api-key", 29 | Required = false, 30 | HelpText = "The API Key to use", 31 | DefaultValue = null)] 32 | public string APIKey { get; set; } 33 | 34 | [Option('e', 35 | "encryption-key", 36 | Required = false, 37 | HelpText = "The Encryption Key to use", 38 | DefaultValue = null)] 39 | public string EncryptionKey { get; set; } 40 | 41 | [Option('d', 42 | "packages-directory", 43 | Required = false, 44 | HelpText = "Defines the directory that contains the to install packages.", 45 | DefaultValue = null)] 46 | public string PackagesDirectoryPath { get; set; } 47 | 48 | [Option('t', 49 | "installation-status-timeout", 50 | Required = false, 51 | HelpText = "The number of seconds to ignore 404 errors when checking installation status", 52 | DefaultValue = 60)] 53 | public double InstallationStatusTimeout { get; set; } 54 | 55 | [HelpOption] 56 | public string GetUsage() 57 | { 58 | return HelpText.AutoBuild(this, 59 | (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DeployClient/PackageCrawler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.IO.Abstractions; 4 | using System.Linq; 5 | 6 | namespace DeployClient 7 | { 8 | internal class PackageCrawler 9 | { 10 | private readonly IFileSystem _fileSystem; 11 | private readonly IDirectoryInfo _packageDirectoryInfo; 12 | 13 | 14 | 15 | /// 16 | /// The directory that contains to installing files. 17 | /// 18 | public string PackageDirectoryPath => _packageDirectoryInfo.FullName; 19 | 20 | 21 | 22 | public PackageCrawler(string packageDirectoryPath) : this(new FileSystem(), packageDirectoryPath) 23 | { 24 | } 25 | 26 | public PackageCrawler(IFileSystem fileSystem, string packageDirectoryPath) 27 | { 28 | _fileSystem = fileSystem; 29 | 30 | _packageDirectoryInfo = GetPackageDirectory(packageDirectoryPath); 31 | } 32 | 33 | 34 | 35 | private IDirectoryInfo GetPackageDirectory(string packageDirectoryPath) 36 | { 37 | var path = string.IsNullOrWhiteSpace(packageDirectoryPath) 38 | ? _fileSystem.Directory.GetCurrentDirectory() 39 | : packageDirectoryPath; 40 | 41 | var directoryInfo = _fileSystem.DirectoryInfo.FromDirectoryName(path); 42 | 43 | if (!directoryInfo.Exists) 44 | { 45 | throw new DirectoryNotFoundException($"Directory \"{directoryInfo.FullName}\" not found."); 46 | } 47 | 48 | return directoryInfo; 49 | } 50 | 51 | /// 52 | /// Returns all installation packages (*.zip files) full file paths which were found in defined . 53 | /// 54 | /// Enumerable of full file paths. 55 | internal IEnumerable GetPackagesFullPaths() 56 | { 57 | const string searchPattern = "*.zip"; 58 | 59 | return _packageDirectoryInfo.GetFiles(searchPattern, SearchOption.TopDirectoryOnly).Select(f => f.FullName); 60 | 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /DeployClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DeployClient")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DeployClient")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b6122ccd-75f9-4def-8daa-e11789d6d6d8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.9.3.0")] 36 | [assembly: AssemblyFileVersion("0.9.3.0")] 37 | 38 | [assembly: InternalsVisibleTo("DeployClient.Tests")] 39 | -------------------------------------------------------------------------------- /DeployClient/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DeployClient.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.ApplicationScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string TargetUri { 30 | get { 31 | return ((string)(this["TargetUri"])); 32 | } 33 | } 34 | 35 | [global::System.Configuration.ApplicationScopedSettingAttribute()] 36 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 37 | [global::System.Configuration.DefaultSettingValueAttribute("")] 38 | public string APIKey { 39 | get { 40 | return ((string)(this["APIKey"])); 41 | } 42 | } 43 | 44 | [global::System.Configuration.ApplicationScopedSettingAttribute()] 45 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 46 | [global::System.Configuration.DefaultSettingValueAttribute("")] 47 | public string EncryptionKey { 48 | get { 49 | return ((string)(this["EncryptionKey"])); 50 | } 51 | } 52 | 53 | [global::System.Configuration.ApplicationScopedSettingAttribute()] 54 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 55 | [global::System.Configuration.DefaultSettingValueAttribute("")] 56 | public string PackagesDirectory { 57 | get { 58 | return ((string)(this["PackagesDirectory"])); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DeployClient/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /DeployClient/gulpfile.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /* 4 | Configuration 5 | Load the project configuration file. 6 | */ 7 | var CONFIG = require('./project.config'); 8 | 9 | /* 10 | Paths 11 | Create all the paths we need ahead of time. 12 | */ 13 | var binDir = 'bin/Release/', 14 | compiledModulesDir = '../dist/'; 15 | 16 | /* 17 | Modules 18 | Require the modules we need. 19 | */ 20 | var gulp = require('gulp'), 21 | clean = require('gulp-clean'), 22 | zip = require('gulp-zip'); 23 | 24 | /* 25 | Visual Studio Integration 26 | These tasks are to provide integration with Visual Studio through pre and 27 | post build events. 28 | */ 29 | gulp.task('pre-build-Release', cleanBin); 30 | gulp.task('post-build-Release', buildRelease); 31 | 32 | gulp.task('pre-build-Debug', noOp); 33 | gulp.task('post-build-Debug', noOp); 34 | 35 | gulp.task('pre-build-Clients', noOp); 36 | gulp.task('post-build-Clients', noOp); 37 | 38 | /* 39 | Clean Bin 40 | You can't rely on Visual Studio to clean the bin folder, even when calling 41 | for the project to be cleaned. This task cleans the bin. 42 | Doesn't bother reading the directory as that slows the task down. 43 | */ 44 | function cleanBin() { 45 | return gulp.src( 46 | `${binDir}*`, 47 | { 48 | read: false 49 | }) 50 | .pipe(clean()); 51 | } 52 | 53 | /* 54 | Build Release 55 | Create a release zip. 56 | */ 57 | function buildRelease() { 58 | return gulp.src( 59 | [ 60 | `${binDir}**` 61 | ]) 62 | 63 | // Zip in to install zip. 64 | .pipe(zip(`DeployClient_${CONFIG.MODULE_VERSION}.zip`)) 65 | 66 | // Move to compiled modules folder. 67 | .pipe(gulp.dest(compiledModulesDir)); 68 | } 69 | 70 | function noOp(done) { 71 | done(); 72 | } -------------------------------------------------------------------------------- /DeployClient/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deploy-client", 3 | "version": "0.9.3", 4 | "main": "gulpfile.js", 5 | "license": "Apache-2.0", 6 | "private": true, 7 | "devDependencies": { 8 | "gulp": "^4.0.2", 9 | "gulp-clean": "^0.4.0", 10 | "gulp-zip": "^5.0.2" 11 | }, 12 | "dependencies": {} 13 | } 14 | -------------------------------------------------------------------------------- /DeployClient/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DeployClient/project.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | MODULE_VERSION: '00.09.03' 3 | }; 4 | -------------------------------------------------------------------------------- /Encryption/CryptoUtilities.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace Cantarus.Libraries.Encryption 5 | { 6 | /// 7 | /// Provides useful utility methods which may not easily be grouped 8 | /// elsewhere. 9 | /// 10 | public static class CryptoUtilities 11 | { 12 | /// 13 | /// Generates a byte array of the length specified filled with random 14 | /// bytes. 15 | /// 16 | /// 17 | /// 18 | public static byte[] GenerateRandomBytes(int length) 19 | { 20 | // Create a new byte array of the size required. 21 | byte[] bytes = new byte[length]; 22 | 23 | using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider()) 24 | { 25 | // Fill it with random bytes. 26 | rngCsp.GetBytes(bytes); 27 | } 28 | 29 | return bytes; 30 | } 31 | 32 | public static string SHA256HashString(string value) 33 | { 34 | byte[] bytes = SHA256HashBytes(value); 35 | 36 | string hash = ""; 37 | 38 | for(int i = 0; i< bytes.Length; i++) 39 | { 40 | hash = string.Format("{0}{1:X2}", hash, bytes[i]); 41 | } 42 | 43 | return hash; 44 | } 45 | 46 | /// 47 | /// Hashes the passed value using the SHA256 algorithm. 48 | /// 49 | /// 50 | /// 51 | public static byte[] SHA256HashBytes(string value) 52 | { 53 | // Convert string to byte array. 54 | byte[] bytes = Encoding.UTF8.GetBytes(value); 55 | 56 | byte[] hashedBytes; 57 | 58 | using (SHA256 sha = new SHA256Managed()) 59 | { 60 | // Hash bytes. 61 | hashedBytes = sha.ComputeHash(bytes); 62 | } 63 | 64 | return hashedBytes; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Encryption/Encryption.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {AB5A3320-F260-42EE-8F19-CCF7546CA511} 8 | Library 9 | Properties 10 | Cantarus.Libraries.Encryption 11 | Cantarus.Libraries.Encryption 12 | v4.8 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Encryption/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Encryption")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Encryption")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ab5a3320-f260-42ee-8f19-ccf7546ca511")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EncryptionTests/EncryptionTests.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Libraries.Encryption; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace EncryptionTests 7 | { 8 | [TestClass] 9 | public class EncryptionTests 10 | { 11 | private const int Iterations = 100; 12 | 13 | [TestMethod] 14 | public void EncryptString_RandomString_ObfuscatedAfterEncryption() 15 | { 16 | for (int i = 0; i < Iterations; i++) 17 | { 18 | string passPhrase = TestUtilities.GeneratePassPhrase(); 19 | string beforeString = Encoding.UTF8.GetString(TestUtilities.GeneratePayload()); 20 | 21 | string encryptedString = Crypto.Encrypt(beforeString, passPhrase); 22 | 23 | Assert.AreNotEqual(beforeString, encryptedString); 24 | } 25 | } 26 | 27 | [TestMethod] 28 | public void EncryptBytes_RandomBytes_ObfuscatedAfterEncryption() 29 | { 30 | for (int i = 0; i < Iterations; i++) 31 | { 32 | string passPhrase = TestUtilities.GeneratePassPhrase(); 33 | byte[] beforeBytes = TestUtilities.GeneratePayload(); 34 | 35 | byte[] encryptedBytes = Crypto.Encrypt(beforeBytes, passPhrase); 36 | 37 | CollectionAssert.AreNotEqual(beforeBytes, encryptedBytes); 38 | } 39 | } 40 | 41 | [TestMethod] 42 | public void EncryptStream_StreamOfRandomBytes_ObfuscatedAfterEncryption() 43 | { 44 | for (int i = 0; i < Iterations; i++) 45 | { 46 | string passPhrase = TestUtilities.GeneratePassPhrase(); 47 | byte[] beforeBytes = TestUtilities.GeneratePayload(); 48 | byte[] encryptedBytes; 49 | 50 | using (MemoryStream plainStream = new MemoryStream(beforeBytes)) 51 | { 52 | using (MemoryStream encryptedSteam = (MemoryStream)Crypto.Encrypt(plainStream, passPhrase)) 53 | { 54 | encryptedBytes = encryptedSteam.ToArray(); 55 | } 56 | } 57 | 58 | CollectionAssert.AreNotEqual(beforeBytes, encryptedBytes); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /EncryptionTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("EncryptionTests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("EncryptionTests")] 10 | [assembly: AssemblyCopyright("Copyright © 2019")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("566818d4-11ae-4c96-821e-91c1c616d19b")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /EncryptionTests/RoundTripTests.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Libraries.Encryption; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace EncryptionTests 7 | { 8 | [TestClass] 9 | public class RoundTripTests 10 | { 11 | private const int Iterations = 100; 12 | 13 | [TestMethod] 14 | public void RoundTrip_RandomString_MatchesAfterEncryptDecrypt() 15 | { 16 | for (int i = 0; i < Iterations; i++) 17 | { 18 | string passPhrase = TestUtilities.GeneratePassPhrase(); 19 | string beforeString = Encoding.UTF8.GetString(TestUtilities.GeneratePayload()); 20 | 21 | string encryptedString = Crypto.Encrypt(beforeString, passPhrase); 22 | 23 | string afterString = Crypto.Decrypt(encryptedString, passPhrase); 24 | 25 | Assert.AreEqual(beforeString, afterString); 26 | } 27 | } 28 | 29 | [TestMethod] 30 | public void RoundTrip_RandomBytes_MatchesAfterEncryptDecrypt() 31 | { 32 | for (int i = 0; i < Iterations; i++) 33 | { 34 | string passPhrase = TestUtilities.GeneratePassPhrase(); 35 | byte[] beforeBytes = TestUtilities.GeneratePayload(); 36 | 37 | byte[] encryptedBytes = Crypto.Encrypt(beforeBytes, passPhrase); 38 | 39 | byte[] afterBytes = Crypto.Decrypt(encryptedBytes, passPhrase); 40 | 41 | CollectionAssert.AreEqual(beforeBytes, afterBytes); 42 | } 43 | } 44 | 45 | [TestMethod] 46 | public void RoundTrip_RandomStreamOfBytes_MatchesAfterEncryptDecrypt() 47 | { 48 | for (int i = 0; i < Iterations; i++) 49 | { 50 | string passPhrase = TestUtilities.GeneratePassPhrase(); 51 | byte[] beforeBytes = TestUtilities.GeneratePayload(); 52 | byte[] afterBytes; 53 | 54 | using (MemoryStream plainStream = new MemoryStream(beforeBytes)) 55 | { 56 | using (Stream encryptedSteam = Crypto.Encrypt(plainStream, passPhrase)) 57 | { 58 | using (MemoryStream decryptedStream = (MemoryStream)Crypto.Decrypt(encryptedSteam, passPhrase)) 59 | { 60 | afterBytes = decryptedStream.ToArray(); 61 | } 62 | } 63 | } 64 | 65 | CollectionAssert.AreEqual(beforeBytes, afterBytes); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /EncryptionTests/TestUtilities.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Text; 4 | 5 | namespace EncryptionTests 6 | { 7 | [TestClass] 8 | internal static class TestUtilities 9 | { 10 | private static Random rand; 11 | 12 | private static Random Rand 13 | { 14 | get 15 | { 16 | if (rand == null) 17 | { 18 | rand = new Random(); 19 | } 20 | 21 | return rand; 22 | } 23 | } 24 | 25 | public static string GeneratePassPhrase() 26 | { 27 | return GenerateRandomString(Rand.Next(8, (256 + 1))); 28 | } 29 | 30 | public static byte[] GeneratePayload() 31 | { 32 | return GenerateRandomBytes(Rand.Next(16, (512 + 1))); 33 | } 34 | 35 | private static string GenerateRandomString(int length) 36 | { 37 | byte[] bytes = new byte[length]; 38 | 39 | for (int i = 0; i < bytes.Length; i++) 40 | { 41 | int min; 42 | int max; 43 | 44 | if (Rand.Next(0, 2) > 0) 45 | { 46 | min = 65; 47 | max = 90; 48 | } 49 | else 50 | { 51 | min = 97; 52 | max = 122; 53 | } 54 | 55 | int num = Rand.Next(min, max); 56 | 57 | bytes[i] = (byte)num; 58 | } 59 | 60 | return Encoding.UTF8.GetString(bytes); 61 | } 62 | 63 | private static byte[] GenerateRandomBytes(int length) 64 | { 65 | byte[] bytes = new byte[length]; 66 | 67 | Rand.NextBytes(bytes); 68 | 69 | return bytes; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /EncryptionTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/AssertionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient.Tests 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using Shouldly; 8 | 9 | public static class AssertionExtensions 10 | { 11 | public static void ShouldHaveApiKeyHeader(this HttpRequestMessage request, string apiKey) 12 | { 13 | request.Headers.TryGetValues("x-api-key", out var apiKeys).ShouldBeTrue(); 14 | apiKeys.ShouldHaveSingleItem().ShouldBe(apiKey); 15 | } 16 | 17 | public static void ShouldContainStringsInOrder(this string str, params string[] stringsToMatch) 18 | { 19 | str.ShouldContainStringsInOrder(onlyOnce: false, stringsToMatch); 20 | } 21 | 22 | public static void ShouldContainStringsInOrder(this string str, bool onlyOnce, params string[] stringsToMatch) 23 | { 24 | var lastIndex = 0; 25 | var matchedIndexes = new HashSet(); 26 | foreach (var stringToMatch in stringsToMatch) 27 | { 28 | str.Substring(lastIndex).ShouldContain(stringToMatch); 29 | lastIndex = str.IndexOf(stringToMatch, lastIndex, StringComparison.Ordinal); 30 | 31 | matchedIndexes.Add(lastIndex); 32 | 33 | lastIndex += stringToMatch.Length; 34 | } 35 | 36 | if (onlyOnce) 37 | { 38 | foreach (var stringToMatch in stringsToMatch) 39 | { 40 | var allIndexes = str.FindAllIndexesOf(stringToMatch).ToList(); 41 | matchedIndexes.IsSupersetOf(allIndexes).ShouldBeTrue(customMessage: $"{stringToMatch} was found more than once."); 42 | } 43 | } 44 | } 45 | 46 | public static void ShouldNotContainStringsInOrder(this string str, params string[] stringsToMatch) 47 | { 48 | var lastIndex = 0; 49 | foreach (var stringToMatch in stringsToMatch) 50 | { 51 | if (!str.Substring(lastIndex).Contains(stringToMatch)) 52 | { 53 | return; 54 | } 55 | lastIndex = str.IndexOf(stringToMatch, lastIndex, StringComparison.Ordinal); 56 | lastIndex += stringToMatch.Length; 57 | } 58 | 59 | str.ShouldNotContain(stringsToMatch.Last()); 60 | } 61 | 62 | private static IEnumerable FindAllIndexesOf(this string str, string stringToMatch) 63 | { 64 | var lastIndex = 0; 65 | 66 | while (lastIndex != -1) 67 | { 68 | lastIndex = str.IndexOf(stringToMatch, lastIndex, StringComparison.Ordinal); 69 | 70 | if (lastIndex == -1) 71 | { 72 | break; 73 | } 74 | 75 | yield return lastIndex; 76 | lastIndex += stringToMatch.Length; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/DeployInputTests.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient.Tests 2 | { 3 | using Spectre.Console; 4 | using Spectre.Console.Cli; 5 | using System.IO.Abstractions; 6 | 7 | public class DeployInputTests 8 | { 9 | [InlineData("", false)] 10 | [InlineData("/test", false)] 11 | [InlineData("https://test.com", true)] 12 | [Theory] 13 | public void Validate_TargetUri(string targetUri, bool isSuccess) 14 | { 15 | var input = TestHelpers.CreateDeployInput(targetUri); 16 | var validate = ValidateInput(input); 17 | 18 | validate.Successful.ShouldBe(isSuccess); 19 | 20 | validate.Message.ShouldBe(isSuccess ? null : "--target-uri must be a valid URI"); 21 | } 22 | 23 | [InlineData(-1, false)] 24 | [InlineData(0, true)] 25 | [InlineData(1, true)] 26 | [Theory] 27 | public void Validate_InstallationStatusTimeout(int timeout, bool isSuccess) 28 | { 29 | var input = TestHelpers.CreateDeployInput(installationStatusTimeout: timeout); 30 | var validate = ValidateInput(input); 31 | 32 | validate.Successful.ShouldBe(isSuccess); 33 | 34 | validate.Message.ShouldBe(isSuccess ? null : "--installation-status-timeout must be non-negative"); 35 | } 36 | 37 | [InlineData("", true)] 38 | [InlineData("='\'", false)] 39 | [InlineData("Dir/Blah", true)] 40 | [Theory] 41 | public void Validate_PackagesDirectoryPath(string packagesDirectoryPath, bool isSuccess) 42 | { 43 | var fileSystem = A.Fake(); 44 | var currentDirectory = Directory.GetCurrentDirectory(); 45 | A.CallTo(() => fileSystem.Directory.Exists("Dir/Blah")).Returns(true); 46 | A.CallTo(() => fileSystem.Directory.Exists(currentDirectory)).Returns(true); 47 | 48 | var input = TestHelpers.CreateDeployInput(packagesDirectoryPath: packagesDirectoryPath); 49 | var validate = ValidateInput(input, fileSystem); 50 | 51 | validate.Successful.ShouldBe(isSuccess); 52 | 53 | validate.Message.ShouldBe(isSuccess ? null : "--packages-directory must be a valid path"); 54 | } 55 | 56 | [InlineData(LogLevel.Trace, true)] 57 | [InlineData(LogLevel.Error, true)] 58 | [InlineData((LogLevel)7, false)] 59 | [InlineData((LogLevel)(-1), false)] 60 | [Theory] 61 | public void Validate_LogLevel(LogLevel logLevel, bool isSuccess) 62 | { 63 | var input = TestHelpers.CreateDeployInput(logLevel: logLevel); 64 | var validate = ValidateInput(input); 65 | 66 | validate.Successful.ShouldBe(isSuccess); 67 | 68 | validate.Message.ShouldBe(isSuccess ? null : "--log-level must be a valid log level"); 69 | } 70 | 71 | private static ValidationResult ValidateInput(DeployInput input, IFileSystem? fileSystem = null) 72 | { 73 | if (fileSystem == null) 74 | { 75 | fileSystem = A.Fake(); 76 | var currentDirectory = Directory.GetCurrentDirectory(); 77 | A.CallTo(() => fileSystem.Directory.Exists(currentDirectory)).Returns(true); 78 | } 79 | 80 | var command = new DeployCommand(A.Fake(), fileSystem); 81 | return command.Validate(A.Dummy(), input); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/EncryptorTests.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient.Tests 2 | { 3 | using System.IO; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Encryption; 7 | using Shouldly; 8 | using Xunit; 9 | 10 | public class EncryptorTests 11 | { 12 | [Fact] 13 | public async Task GetEncryptedStream_EncryptsFileContents() 14 | { 15 | var deployInput = TestHelpers.CreateDeployInput(encryptionKey: "abcd1234"); 16 | var encryptor = new Encryptor(); 17 | 18 | var encryptedStream = await encryptor.GetEncryptedStream(deployInput, new MemoryStream(Encoding.UTF8.GetBytes("ZIP"))); 19 | 20 | var decryptedStream = Crypto.Decrypt(encryptedStream, deployInput.EncryptionKey); 21 | var decryptedContents = await new StreamReader(decryptedStream).ReadToEndAsync(); 22 | decryptedContents.ShouldBe("ZIP"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/PackageFileSourceTests.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient.Tests 2 | { 3 | using System.IO.Abstractions; 4 | 5 | public class PackageFileSourceTests 6 | { 7 | [InlineData("")] 8 | [InlineData("path/to/packages")] 9 | [Theory] 10 | public void GetPackageFiles_GetsTheZipFilesInTheGivenDirectory(string path) 11 | { 12 | var fileSystem = A.Fake(); 13 | 14 | A.CallTo(() => fileSystem.Directory.GetFiles(path, "*.zip")).Returns(new[] { "package1.zip", "package2.zip" }); 15 | 16 | var fileSource = new PackageFileSource(fileSystem); 17 | var files = fileSource.GetPackageFiles(path); 18 | 19 | files.ShouldBe(new[] { "package1.zip", "package2.zip" }, ignoreOrder: true); 20 | } 21 | 22 | [Fact] 23 | public void GetFileStream_ReturnsStream() 24 | { 25 | var fileSystem = A.Fake(); 26 | 27 | var stream = new FakeStream(); 28 | A.CallTo(() => fileSystem.File.Open("package1.zip", FileMode.Open, FileAccess.Read)).Returns(stream); 29 | 30 | var fileSource = new PackageFileSource(fileSystem); 31 | var fileStream = fileSource.GetFileStream("package1.zip"); 32 | 33 | fileStream.ShouldBe(stream); 34 | } 35 | 36 | private class FakeStream : FileSystemStream 37 | { 38 | public FakeStream(Stream? stream = null, string? path = null, bool isAsync = false) : base(stream ?? new MemoryStream(), path ?? ".", isAsync) 39 | { 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/PolyDeploy.DeployClient.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | false 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | all 28 | runtime; build; native; contentfiles; analyzers; buildtransitive 29 | 30 | 31 | 32 | 33 | 34 | runtime; build; native; contentfiles; analyzers; buildtransitive 35 | all 36 | 37 | 38 | runtime; build; native; contentfiles; analyzers; buildtransitive 39 | all 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/TestHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient.Tests; 2 | 3 | public static class TestHelpers 4 | { 5 | public static DeployInput CreateDeployInput(string? targetUri = null, string? apiKey = null, string? encryptionKey = null, int? installationStatusTimeout = null, string? packagesDirectoryPath = null, LogLevel? logLevel = null) 6 | { 7 | return new DeployInput 8 | { 9 | TargetUri = targetUri ?? "https://test.com", 10 | ApiKey = apiKey ?? A.Dummy(), 11 | EncryptionKey = encryptionKey ?? A.Dummy(), 12 | InstallationStatusTimeout = installationStatusTimeout ?? 0, 13 | PackagesDirectoryPath = packagesDirectoryPath ?? A.Dummy(), 14 | LogLevel = logLevel ?? A.Dummy() 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient.Tests/TestStopwatch.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient.Tests; 2 | using System; 3 | using System.Collections.Generic; 4 | using Shouldly; 5 | 6 | public class TestStopwatch : IStopwatch 7 | { 8 | private readonly IReadOnlyList timeSpans; 9 | 10 | public TestStopwatch(params TimeSpan[] timeSpans) 11 | { 12 | this.timeSpans = timeSpans; 13 | } 14 | 15 | private int elapsedCalled; 16 | 17 | public TimeSpan Elapsed 18 | { 19 | get 20 | { 21 | this.IsStartNewCalled.ShouldBeTrue(); 22 | 23 | if (this.timeSpans.Count == 0) 24 | { 25 | return TimeSpan.Zero; 26 | } 27 | 28 | return elapsedCalled >= this.timeSpans.Count 29 | ? this.timeSpans[^1] 30 | : timeSpans[elapsedCalled++]; 31 | } 32 | } 33 | 34 | public bool IsStartNewCalled { get; private set; } 35 | 36 | public void StartNew() 37 | { 38 | this.IsStartNewCalled = true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/Delayer.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | public class Delayer : IDelayer 7 | { 8 | public Task Delay(TimeSpan delay) 9 | { 10 | return Task.Delay(delay); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/DeployCommand.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.IO.Abstractions; 4 | 5 | using Spectre.Console.Cli; 6 | using Spectre.Console; 7 | 8 | public class DeployCommand : AsyncCommand 9 | { 10 | private readonly IFileSystem fileSystem; 11 | private readonly IDeployer deployer; 12 | 13 | public DeployCommand(IDeployer deployer, IFileSystem fileSystem) 14 | { 15 | this.deployer = deployer; 16 | this.fileSystem = fileSystem; 17 | } 18 | 19 | public override async Task ExecuteAsync(CommandContext context, DeployInput input) 20 | { 21 | var exitCode = await this.deployer.StartAsync(input); 22 | return (int)exitCode; 23 | } 24 | 25 | public override ValidationResult Validate(CommandContext context, DeployInput settings) 26 | { 27 | if (!string.IsNullOrWhiteSpace(settings.PackagesDirectoryPath) && !this.fileSystem.Directory.Exists(settings.PackagesDirectoryPath)) 28 | { 29 | return ValidationResult.Error("--packages-directory must be a valid path"); 30 | } 31 | 32 | if (!Uri.TryCreate(settings.TargetUri, UriKind.Absolute, out _)) 33 | { 34 | return ValidationResult.Error("--target-uri must be a valid URI"); 35 | } 36 | 37 | if (settings.InstallationStatusTimeout < 0) 38 | { 39 | return ValidationResult.Error("--installation-status-timeout must be non-negative"); 40 | } 41 | 42 | if (!Enum.GetValues().Contains(settings.LogLevel)) 43 | { 44 | return ValidationResult.Error("--log-level must be a valid log level"); 45 | } 46 | 47 | return ValidationResult.Success(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/DeployInput.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.ComponentModel; 4 | 5 | using Spectre.Console.Cli; 6 | 7 | public enum ExitCode 8 | { 9 | Success = 0, 10 | InstallerError = 1, 11 | PackageError = 2, 12 | UnexpectedError = int.MaxValue 13 | } 14 | 15 | public class DeployInput : CommandSettings 16 | { 17 | private string packagesDirectoryPath = string.Empty; 18 | 19 | [CommandOption("-u|--target-uri")] 20 | [Description("The URL of the site to which the packages will be deployed.")] 21 | public string TargetUri { get; set; } = string.Empty; 22 | 23 | [CommandOption("-a|--api-key")] 24 | [Description("The key used to authenticate with the web server.")] 25 | public string ApiKey { get; set; } = string.Empty; 26 | 27 | [CommandOption("-e|--encryption-key")] 28 | [Description("The key used to encrypt the packages before uploading.")] 29 | public string EncryptionKey { get; set; } = string.Empty; 30 | 31 | [CommandOption("-t|--installation-status-timeout")] 32 | [Description("The number of seconds to ignore 404 errors when checking installation status.")] 33 | [DefaultValue(60)] 34 | public int InstallationStatusTimeout { get; set; } 35 | 36 | [CommandOption("-d|--packages-directory")] 37 | [Description("Defines the directory that contains the to install packages.")] 38 | public string PackagesDirectoryPath 39 | { 40 | get => ValidOrCurrentDirectory(this.packagesDirectoryPath); 41 | set => this.packagesDirectoryPath = ValidOrCurrentDirectory(value); 42 | } 43 | 44 | [CommandOption("-l|--log-level")] 45 | [Description("Defines the amount of logging.")] 46 | [DefaultValue(LogLevel.Information)] 47 | public LogLevel LogLevel { get; set; } 48 | 49 | public Uri GetTargetUri() => new Uri(this.TargetUri, UriKind.Absolute); 50 | 51 | private static string ValidOrCurrentDirectory(string path) 52 | { 53 | return string.IsNullOrEmpty(path) ? Directory.GetCurrentDirectory() : path; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/Deployer.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | public class Deployer : IDeployer 4 | { 5 | private readonly IRenderer renderer; 6 | private readonly IPackageFileSource packageFileSource; 7 | private readonly IInstaller installer; 8 | private readonly IEncryptor encryptor; 9 | private readonly IDelayer delayer; 10 | 11 | public Deployer(IRenderer renderer, IPackageFileSource packageFileSource, IInstaller installer, IEncryptor encryptor, IDelayer delayer) 12 | { 13 | this.renderer = renderer; 14 | this.packageFileSource = packageFileSource; 15 | this.installer = installer; 16 | this.encryptor = encryptor; 17 | this.delayer = delayer; 18 | } 19 | 20 | public async Task StartAsync(DeployInput options) 21 | { 22 | try 23 | { 24 | this.renderer.Welcome(options.LogLevel); 25 | 26 | var packageFiles = this.packageFileSource.GetPackageFiles(options.PackagesDirectoryPath); 27 | this.renderer.RenderListOfFiles(options.LogLevel, packageFiles); 28 | 29 | var sessionId = await this.installer.StartSessionAsync(options); 30 | 31 | var uploads = packageFiles.Select(file => (file, this.UploadPackage(sessionId, file, options))); 32 | await this.renderer.RenderFileUploadsAsync(options.LogLevel, uploads); 33 | 34 | _ = this.installer.InstallPackagesAsync(options, sessionId); 35 | 36 | var hasRenderedOverview = false; 37 | while (true) 38 | { 39 | var session = await this.installer.GetSessionAsync(options, sessionId); 40 | if (session.Responses != null) 41 | { 42 | if (!hasRenderedOverview) 43 | { 44 | this.renderer.RenderInstallationOverview(options.LogLevel, session.Responses); 45 | hasRenderedOverview = true; 46 | } 47 | 48 | this.renderer.RenderInstallationStatus(options.LogLevel, session.Responses); 49 | } 50 | 51 | if (session.Status == SessionStatus.Complete) 52 | { 53 | if (session.Responses?.Any(r => !r.Value?.Success == true) == true) 54 | { 55 | return ExitCode.PackageError; 56 | } 57 | 58 | return ExitCode.Success; 59 | } 60 | 61 | await this.delayer.Delay(TimeSpan.FromSeconds(1)); 62 | } 63 | } 64 | catch (InstallerException e) 65 | { 66 | this.renderer.RenderCriticalError(options.LogLevel, e.Message, e.InnerException!); 67 | return ExitCode.InstallerError; 68 | } 69 | catch (Exception e) 70 | { 71 | this.renderer.RenderCriticalError(options.LogLevel, "An unexpected error occurred.", e); 72 | return ExitCode.UnexpectedError; 73 | } 74 | } 75 | 76 | private async Task UploadPackage(string sessionId, string packageFile, DeployInput options) 77 | { 78 | await using var packageFileStream = this.packageFileSource.GetFileStream(packageFile); 79 | await using var encryptedPackageStream = await this.encryptor.GetEncryptedStream(options, packageFileStream); 80 | 81 | await this.installer.UploadPackageAsync(options, sessionId, encryptedPackageStream, packageFile); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/Encryptor.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using Encryption; 6 | 7 | public class Encryptor : IEncryptor 8 | { 9 | public Task GetEncryptedStream(DeployInput options, Stream packageStream) 10 | { 11 | return Task.FromResult(Crypto.Encrypt(packageStream, options.EncryptionKey)); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IDelayer.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | public interface IDelayer 7 | { 8 | Task Delay(TimeSpan delay); 9 | } 10 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IDeployer.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.Threading.Tasks; 4 | 5 | public interface IDeployer 6 | { 7 | Task StartAsync(DeployInput options); 8 | } 9 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IEncryptor.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | public interface IEncryptor 7 | { 8 | Task GetEncryptedStream(DeployInput options, Stream packageStream); 9 | } 10 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IInstaller.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | public interface IInstaller 7 | { 8 | Task StartSessionAsync(DeployInput options); 9 | 10 | Task GetSessionAsync(DeployInput options, string sessionId); 11 | 12 | Task UploadPackageAsync(DeployInput options, string sessionId, Stream encryptedPackage, string packageName); 13 | 14 | Task InstallPackagesAsync(DeployInput options, string sessionId); 15 | } 16 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IPackageFileSource.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | public interface IPackageFileSource 7 | { 8 | IReadOnlyCollection GetPackageFiles(string path); 9 | 10 | Stream GetFileStream(string fileName); 11 | } 12 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IRenderer.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | public interface IRenderer 8 | { 9 | void Welcome(LogLevel level); 10 | void RenderListOfFiles(LogLevel level, IEnumerable files); 11 | Task RenderFileUploadsAsync(LogLevel level, IEnumerable<(string file, Task uploadTask)> uploads); 12 | void RenderInstallationOverview(LogLevel level, SortedList packageFiles); 13 | void RenderInstallationStatus(LogLevel level, SortedList packageFiles); 14 | void RenderCriticalError(LogLevel level, string message, Exception exception); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/IStopwatch.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PolyDeploy.DeployClient 3 | { 4 | using System; 5 | 6 | public interface IStopwatch 7 | { 8 | void StartNew(); 9 | 10 | TimeSpan Elapsed { get; } 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/InstallerException.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient; 2 | 3 | using System; 4 | public class InstallerException : Exception 5 | { 6 | public InstallerException(string message, Exception innerException) 7 | : base(message, innerException) 8 | {} 9 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/LogLevel.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | public enum LogLevel 4 | { 5 | Trace = 0, 6 | Debug = 1, 7 | Information = 2, 8 | Warning = 3, 9 | Error = 4, 10 | Critical = 5, 11 | None = 6 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/PackageFileSource.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | using System.Collections.Generic; 4 | using System.IO.Abstractions; 5 | using System.IO; 6 | 7 | public class PackageFileSource : IPackageFileSource 8 | { 9 | private readonly IFileSystem fileSystem; 10 | 11 | public PackageFileSource(IFileSystem fileSystem) 12 | { 13 | this.fileSystem = fileSystem; 14 | } 15 | 16 | public IReadOnlyCollection GetPackageFiles(string path) 17 | { 18 | return this.fileSystem.Directory.GetFiles(path, "*.zip"); 19 | } 20 | 21 | public Stream GetFileStream(string fileName) 22 | { 23 | return this.fileSystem.File.Open(fileName, FileMode.Open, FileAccess.Read); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/PolyDeploy.DeployClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | true 8 | latest 9 | 10 | true 11 | polydeploy 12 | 1.0.0 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | all 31 | runtime; build; native; contentfiles; analyzers; buildtransitive 32 | 33 | 34 | 35 | all 36 | runtime; build; native; contentfiles; analyzers; buildtransitive 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using PolyDeploy.DeployClient; 4 | using Spectre.Console; 5 | using Spectre.Console.Cli; 6 | 7 | try 8 | { 9 | var services = new ServiceCollection(); 10 | services.AddTransient(_ => AnsiConsole.Console); 11 | services.AddHttpClient(); 12 | services.AddTransient(); 13 | services.AddTransient(); 14 | services.AddTransient(); 15 | services.AddTransient(); 16 | services.AddTransient(); 17 | services.AddTransient(); 18 | services.AddTransient(); 19 | 20 | var registrar = new ServiceCollectionRegistrar(services); 21 | var app = new CommandApp(registrar); 22 | return app.Run(args); 23 | } 24 | catch 25 | { 26 | await Task.Delay(TimeSpan.FromSeconds(1)); 27 | return 1; 28 | } 29 | -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/Session.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.DeployClient 2 | { 3 | public enum SessionStatus 4 | { 5 | NotStarted = 0, 6 | InProgess = 1, 7 | Complete = 2, 8 | } 9 | 10 | public class Session 11 | { 12 | public SessionStatus Status { get; init; } 13 | public SortedList? Responses { get; set; } 14 | } 15 | 16 | public record SessionResponse 17 | { 18 | public string? Name { get; init; } 19 | public List? Packages { get; init; } 20 | public List? Failures { get; init; } 21 | public bool Attempted { get; init; } 22 | public bool Success { get; init; } 23 | public bool CanInstall { get; init; } 24 | } 25 | 26 | public class PackageResponse 27 | { 28 | public string? Name { get; init; } 29 | public List? Dependencies { get; init; } 30 | public string? VersionStr { get; init; } 31 | public bool CanInstall { get; init; } 32 | } 33 | 34 | public class DependencyResponse 35 | { 36 | public bool IsPackageDependency { get; init; } 37 | public string? PackageName { get; init; } 38 | public string? DependencyVersion { get; init; } 39 | } 40 | } -------------------------------------------------------------------------------- /PolyDeploy.DeployClient/Stopwatch.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PolyDeploy.DeployClient 3 | { 4 | using System; 5 | 6 | public class Stopwatch : IStopwatch 7 | { 8 | public void StartNew() 9 | { 10 | this.stopwatch = System.Diagnostics.Stopwatch.StartNew(); 11 | } 12 | 13 | public TimeSpan Elapsed { get { return this.stopwatch?.Elapsed ?? TimeSpan.Zero; } } 14 | 15 | private System.Diagnostics.Stopwatch? stopwatch; 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /PolyDeploy.Encryption/CryptoUtilities.cs: -------------------------------------------------------------------------------- 1 | namespace PolyDeploy.Encryption 2 | { 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | /// 7 | /// Provides useful utility methods which may not easily be grouped 8 | /// elsewhere. 9 | /// 10 | public static class CryptoUtilities 11 | { 12 | /// 13 | /// Generates a byte array of the length specified filled with random 14 | /// bytes. 15 | /// 16 | /// 17 | /// 18 | public static byte[] GenerateRandomBytes(int length) 19 | { 20 | // Create a new byte array of the size required. 21 | var bytes = new byte[length]; 22 | 23 | using RNGCryptoServiceProvider rngCsp = new(); 24 | // Fill it with random bytes. 25 | rngCsp.GetBytes(bytes); 26 | 27 | return bytes; 28 | } 29 | 30 | public static string SHA256HashString(string value) 31 | { 32 | var bytes = SHA256HashBytes(value); 33 | 34 | return bytes.Aggregate("", (current, part) => $"{current}{part:X2}"); 35 | } 36 | 37 | /// 38 | /// Hashes the passed value using the SHA256 algorithm. 39 | /// 40 | /// 41 | /// 42 | public static byte[] SHA256HashBytes(string value) 43 | { 44 | // Convert string to byte array. 45 | var bytes = Encoding.UTF8.GetBytes(value); 46 | 47 | using SHA256 sha = new SHA256Managed(); 48 | // Hash bytes. 49 | var hashedBytes = sha.ComputeHash(bytes); 50 | 51 | return hashedBytes; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PolyDeploy.Encryption/PolyDeploy.Encryption.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | true 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /PolyDeploy.Encryption/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | ".NETStandard,Version=v2.0": { 5 | "Microsoft.SourceLink.GitHub": { 6 | "type": "Direct", 7 | "requested": "[1.1.1, )", 8 | "resolved": "1.1.1", 9 | "contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==", 10 | "dependencies": { 11 | "Microsoft.Build.Tasks.Git": "1.1.1", 12 | "Microsoft.SourceLink.Common": "1.1.1" 13 | } 14 | }, 15 | "NETStandard.Library": { 16 | "type": "Direct", 17 | "requested": "[2.0.3, )", 18 | "resolved": "2.0.3", 19 | "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", 20 | "dependencies": { 21 | "Microsoft.NETCore.Platforms": "1.1.0" 22 | } 23 | }, 24 | "Microsoft.Build.Tasks.Git": { 25 | "type": "Transitive", 26 | "resolved": "1.1.1", 27 | "contentHash": "AT3HlgTjsqHnWpBHSNeR0KxbLZD7bztlZVj7I8vgeYG9SYqbeFGh0TM/KVtC6fg53nrWHl3VfZFvb5BiQFcY6Q==" 28 | }, 29 | "Microsoft.NETCore.Platforms": { 30 | "type": "Transitive", 31 | "resolved": "1.1.0", 32 | "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" 33 | }, 34 | "Microsoft.SourceLink.Common": { 35 | "type": "Transitive", 36 | "resolved": "1.1.1", 37 | "contentHash": "WMcGpWKrmJmzrNeuaEb23bEMnbtR/vLmvZtkAP5qWu7vQsY59GqfRJd65sFpBszbd2k/bQ8cs8eWawQKAabkVg==" 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /PolyDeploy.lutconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | 180000 6 | -------------------------------------------------------------------------------- /PolyDeploy.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True -------------------------------------------------------------------------------- /PolyDeploy/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-object-rest-spread"], 3 | "presets": ["es2015"] 4 | } 5 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/css/main.less: -------------------------------------------------------------------------------- 1 | /* 2 | Core Styling 3 | */ 4 | 5 | @path-node-modules: "../../../../node_modules/"; 6 | 7 | #cantarus-poly-deploy { 8 | 9 | // Import namespaced bootstrap. 10 | @import "@{path-node-modules}bootstrap/less/bootstrap.less"; 11 | 12 | // Override old DNN skin. 13 | input { 14 | border: solid 1px rgb(238, 238, 238); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/app.js: -------------------------------------------------------------------------------- 1 | var angular = require('angular'); 2 | 3 | // Create Angular module. 4 | var angularModule = angular.module('cantarus.poly-deploy', []); 5 | 6 | // Values 7 | angularModule.value('baseUrl', '/'); 8 | angularModule.value('apiUrl', '/DesktopModules/PolyDeploy/API/'); 9 | 10 | // Boostrap. 11 | require('./bootstrap')(angularModule); 12 | 13 | module.exports = 'cantarus.poly-deploy'; 14 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | var controllers = require('./controllers'), 2 | services = require('./services'); 3 | 4 | module.exports = function (mod) { 5 | 6 | // Register controllers. 7 | controllers(mod); 8 | 9 | // Register services. 10 | services(mod); 11 | }; 12 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/controllers/WarningController.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$scope', 'WarnService', 2 | function ($scope, WarnService) { 3 | 4 | $scope.warnings = WarnService.warnings; 5 | 6 | }]; 7 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/controllers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Controllers 3 | This file will register all your controllers for you. 4 | */ 5 | 6 | // Build require context of controllers. 7 | var controllersContext = require.context('./', true, /\Controller.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each controller. 12 | controllersContext.keys().forEach(function (controller) { 13 | 14 | // Strip path. 15 | var controllerName = controller.substring(controller.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | controllerName = controllerName.substring(0, controllerName.lastIndexOf('.js')); 19 | 20 | // Register controller with module. 21 | mod.controller(controllerName, controllersContext(controller)); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/APIUserDataService.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$http', 'apiUrl', 2 | function ($http, apiUrl) { 3 | 4 | var controllerUrl = apiUrl + 'APIUser/'; 5 | 6 | // GET 7 | // Get all API users. 8 | function getUsers() { 9 | 10 | // Make request. 11 | return $http.get(controllerUrl + 'GetAll').then( 12 | function (response) { 13 | 14 | // Return unpacked data. 15 | return response.data; 16 | }); 17 | } 18 | 19 | // POST 20 | // Create a new API user. 21 | function createUser(name, bypass) { 22 | 23 | // Make request. 24 | return $http.post(controllerUrl + 'Create?name=' + name + '&bypass=' + !!bypass).then( 25 | function (response) { 26 | 27 | // Return unpacked data. 28 | return response.data; 29 | }); 30 | } 31 | 32 | // PUT 33 | // Update API user. 34 | function updateUser(apiUser) { 35 | 36 | // Make request. 37 | return $http.put(controllerUrl + 'Update', apiUser).then( 38 | function (response) { 39 | 40 | // Return unpacked data. 41 | return response.data; 42 | }); 43 | } 44 | 45 | // DELETE 46 | // Delete API user; 47 | function deleteUser(apiUser) { 48 | console.log('Deleting: '); 49 | console.log(apiUser); 50 | // Make request. 51 | return $http.delete(controllerUrl + 'Delete?id=' + apiUser.APIUserId).then( 52 | function (response) { 53 | 54 | // Return unpacked data. 55 | return response.data; 56 | }); 57 | } 58 | 59 | return { 60 | getUsers: getUsers, 61 | createUser: createUser, 62 | updateUser: updateUser, 63 | deleteUser: deleteUser 64 | }; 65 | 66 | }]; 67 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/DnnService.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$rootElement', 2 | function ($rootElement) { 3 | 4 | // Retrieve module id from data on roor element (The element ng-app is attached to). 5 | var moduleId = $rootElement.data().moduleId; 6 | 7 | // Set up services framework. 8 | var servicesFramework = $.ServicesFramework(moduleId); 9 | 10 | // Get the headers used to authenticate calls to dnn. 11 | function getSecurityHeaders() { 12 | 13 | var headers = { 14 | ModuleId: servicesFramework.getModuleId(), 15 | TabId: servicesFramework.getTabId(), 16 | }; 17 | 18 | var antiForgeryKey = servicesFramework.getAntiForgeryKey(); 19 | 20 | headers[antiForgeryKey] = servicesFramework.getAntiForgeryValue(); 21 | 22 | // for some reason this is what DNN accepts for ValidateAntiForgeryToken 23 | headers["RequestVerificationToken"] = servicesFramework.getAntiForgeryValue(); 24 | 25 | return headers; 26 | }; 27 | 28 | return { 29 | getSecurityHeaders: getSecurityHeaders 30 | }; 31 | }]; 32 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/EventLogDataService.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$http', 'apiUrl', 2 | function ($http, apiUrl) { 3 | 4 | var controllerUrl = apiUrl + 'EventLog/'; 5 | 6 | // GET 7 | // Browse Events. 8 | function browseEvents(options) { 9 | 10 | var queryParameters = {}; 11 | 12 | if (options) { 13 | if (options.pageIndex) { 14 | queryParameters['pageIndex'] = options.pageIndex; 15 | } 16 | 17 | if (options.pageSize) { 18 | queryParameters['pageSize'] = options.pageSize; 19 | } 20 | 21 | if (options.eventType) { 22 | queryParameters['eventType'] = options.eventType; 23 | } 24 | 25 | if (options.severity) { 26 | queryParameters['severity'] = options.severity; 27 | } 28 | } 29 | 30 | var queryString = buildQueryString(queryParameters); 31 | 32 | // Make request. 33 | return $http.get(controllerUrl + 'Browse' + queryString).then( 34 | function (response) { 35 | 36 | // Return unpacked data. 37 | return response.data; 38 | }); 39 | } 40 | 41 | // GET 42 | // Get event types. 43 | function getEventTypes() { 44 | 45 | // Make request. 46 | return $http.get(controllerUrl + 'EventTypes').then( 47 | function (response) { 48 | 49 | // Return unpacked data. 50 | return response.data; 51 | }); 52 | } 53 | 54 | // Builds a query string from the passed object. 55 | function buildQueryString(queryParams) { 56 | 57 | var queryString = ''; 58 | 59 | // Loop properties. 60 | for (var paramName in queryParams) { 61 | 62 | // Is it the first parameter? 63 | if (queryString.length === 0) { 64 | 65 | // Yes, use '?'. 66 | queryString = queryString + '?'; 67 | } else { 68 | 69 | // No, use '&'. 70 | queryString = queryString + '&'; 71 | } 72 | 73 | // Add parameter. 74 | queryString = queryString + paramName + '=' + queryParams[paramName]; 75 | } 76 | 77 | return queryString; 78 | } 79 | 80 | return { 81 | browseEvents: browseEvents, 82 | getEventTypes: getEventTypes 83 | }; 84 | 85 | }]; 86 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/IPSpecDataService.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$http', 'apiUrl', 2 | function ($http, apiUrl) { 3 | 4 | var controllerUrl = apiUrl + 'IPSpec/'; 5 | 6 | // GET 7 | // Get all IPSpecs. 8 | function getSpecs() { 9 | 10 | // Make request. 11 | return $http.get(controllerUrl + 'GetAll').then( 12 | function (response) { 13 | 14 | // Return unpacked data. 15 | return response.data; 16 | }); 17 | } 18 | 19 | // POST 20 | // Create a new IPSpec. 21 | function createSpec(name, ipAddress) { 22 | 23 | // Make request. 24 | return $http.post(controllerUrl + `Create?name=${name}&ip=${ipAddress}`).then( 25 | function (response) { 26 | return { 27 | err: null, 28 | ipSpec: response.data 29 | } 30 | }, 31 | function (response) { 32 | return { 33 | err: response.data.Message 34 | } 35 | }); 36 | } 37 | 38 | // DELETE 39 | // Delete IPSpec; 40 | function deleteSpec(ipSpec) { 41 | 42 | // Make request. 43 | return $http.delete(controllerUrl + 'Delete?id=' + ipSpec.IPSpecId).then( 44 | function (response) { 45 | 46 | // Return unpacked data. 47 | return response.data; 48 | }); 49 | } 50 | 51 | return { 52 | getSpecs: getSpecs, 53 | createSpec: createSpec, 54 | deleteSpec: deleteSpec 55 | }; 56 | 57 | }]; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/SettingDataService.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$http', 'apiUrl', 2 | function ($http, apiUrl) { 3 | 4 | var controllerUrl = apiUrl + 'Setting/'; 5 | 6 | var subscribers = {}; 7 | 8 | // GET 9 | // Get a Setting. 10 | function getSetting(group, key) { 11 | 12 | // Make request. 13 | return $http.get(controllerUrl + `Get?group=${group}&key=${key}`).then( 14 | function (response) { 15 | 16 | // Return unpacked data. 17 | return response.data; 18 | }); 19 | } 20 | 21 | // POST 22 | // Update or create a Setting. 23 | function setSetting(group, key, value) { 24 | 25 | // Make request. 26 | return $http.post(controllerUrl + `Set?group=${group}&key=${key}&value=${value}`).then( 27 | function (response) { 28 | 29 | // Return unpacked data. 30 | return response.data; 31 | }); 32 | } 33 | 34 | function getWhitelistState() { 35 | 36 | var group = 'WHITELIST', 37 | setting = 'STATE'; 38 | 39 | return getSetting(group, setting) 40 | .then(function (result) { 41 | return result.Value.toLowerCase() === 'true'; 42 | }); 43 | } 44 | 45 | function setWhitelistState(value) { 46 | 47 | var group = 'WHITELIST', 48 | setting = 'STATE'; 49 | 50 | return setSetting(group, setting, value) 51 | .then(function () { 52 | 53 | // Notify subscribers of change. 54 | notify(`${group}_${setting}`); 55 | }); 56 | } 57 | 58 | // Allow other services to subscribe to changes to particular settings. 59 | function subscribe(key, callback) { 60 | 61 | // Do we have already have a subscribers array with this key? 62 | if (!subscribers[key]) { 63 | 64 | // No, create it. 65 | subscribers[key] = []; 66 | } 67 | 68 | // Add subscriber. 69 | subscribers[key].push(callback); 70 | } 71 | 72 | // Notify subscribers. 73 | function notify(key, value) { 74 | 75 | // Any subscribers? 76 | if (subscribers[key] && subscribers[key].length > 0) { 77 | 78 | // Notify each. 79 | angular.forEach(subscribers[key], 80 | function (subscriber) { 81 | subscriber(value); 82 | }); 83 | } 84 | } 85 | 86 | return { 87 | subscribe: subscribe, 88 | whitelist: { 89 | getState: getWhitelistState, 90 | setState: setWhitelistState 91 | } 92 | }; 93 | 94 | }]; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/WarnService.js: -------------------------------------------------------------------------------- 1 | module.exports = ['SettingDataService', 2 | function (SettingDataService) { 3 | 4 | var warnings = { 5 | 6 | // IP Whitelist Disabled. 7 | whitelistDisabled: { 8 | key: 'WHITELIST_STATE', 9 | active: false, 10 | message: 'IP Whitelisting is currently disabled.', 11 | test: function () { 12 | var self = this; 13 | 14 | // Retrive state. 15 | SettingDataService.whitelist.getState() 16 | .then(function (result) { 17 | self.active = !result; 18 | }); 19 | } 20 | } 21 | }; 22 | 23 | // For each warning. 24 | angular.forEach(warnings, function (warning) { 25 | 26 | // Is there a test available? 27 | if (warning.test) { 28 | 29 | // Perform test. 30 | warning.test(); 31 | 32 | // Is there a key? 33 | if (warning.key) { 34 | 35 | // Subscribe for updates. 36 | SettingDataService.subscribe(warning.key, 37 | function () { 38 | warning.test(); 39 | }); 40 | } 41 | } 42 | }); 43 | 44 | // Update the state of the passed warning key. 45 | function updateWarnState(warn, state) { 46 | 47 | // Key exists? 48 | if (warnings[warn]) { 49 | warnings[warn].state = state; 50 | } 51 | } 52 | 53 | return { 54 | warnings: warnings 55 | }; 56 | }]; 57 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Core/src/js/services/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Services 3 | This file will register all your services for you. 4 | */ 5 | 6 | // Build require context of controllers. 7 | var servicesContext = require.context('./', true, /\Service.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each service. 12 | servicesContext.keys().forEach(function (service) { 13 | 14 | // Strip path. 15 | var serviceName = service.substring(service.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | serviceName = serviceName.substring(0, serviceName.lastIndexOf('.js')); 19 | 20 | // Register service with module. 21 | mod.factory(serviceName, servicesContext(service)); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/Deploy.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control language="C#" Inherits="Cantarus.Modules.PolyDeploy.Deploy" AutoEventWireup="false" Codebehind="Deploy.ascx.cs" %> 2 | 3 | 4 |
5 |
8 | 9 | 10 |
11 | 12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/Deploy.ascx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DotNetNuke.Services.Exceptions; 3 | using DotNetNuke.Entities.Modules.Actions; 4 | using DotNetNuke.Services.Localization; 5 | using DotNetNuke.Security; 6 | using DotNetNuke.Web.Client.ClientResourceManagement; 7 | using DotNetNuke.Framework; 8 | using Cantarus.Modules.PolyDeploy.Components; 9 | using DotNetNuke.Entities.Modules; 10 | 11 | namespace Cantarus.Modules.PolyDeploy 12 | { 13 | public partial class Deploy : PolyDeployModuleBase, IActionable 14 | { 15 | protected override void OnInit(EventArgs e) 16 | { 17 | ClientResourceManager.RegisterStyleSheet(Page, string.Format("{0}/dist/Deploy.styles.css", TemplateSourceDirectory)); 18 | 19 | ClientResourceManager.RegisterScript(Page, string.Format("{0}/dist/Deploy.bundle.js", TemplateSourceDirectory), 500); 20 | base.OnInit(e); 21 | } 22 | 23 | protected override void OnLoad(EventArgs e) 24 | { 25 | base.OnLoad(e); 26 | 27 | // Register for Services Framework. 28 | ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); 29 | } 30 | 31 | public ModuleActionCollection ModuleActions 32 | { 33 | get 34 | { 35 | ModuleActionCollection Actions = new ModuleActionCollection(); 36 | 37 | Actions.Add(GetNextActionID(), Localization.GetString("Manage", this.LocalResourceFile), "", "", "", EditUrl("Manage"), false, SecurityAccessLevel.Edit, true, false); 38 | 39 | return Actions; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/Deploy.ascx.designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace Cantarus.Modules.PolyDeploy { 11 | 12 | 13 | public partial class Deploy { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/css/main.less: -------------------------------------------------------------------------------- 1 | /* 2 | Deploy 3 | Styling for the Deploy Angular application. 4 | */ 5 | 6 | // Import from Core. 7 | @import "../../../Core/src/css/main.less"; 8 | 9 | #cantarus-poly-deploy { 10 | 11 | #upload { 12 | 13 | .drop-zone { 14 | line-height: 90px; 15 | border: 2px dashed rgba(0,0,0,0.3); 16 | text-align: center; 17 | font-size: 14pt; 18 | color: rgba(0,0,0,0.3); 19 | font-weight: bold; 20 | background: rgba(255,255,255,0.5); 21 | 22 | &:hover { 23 | border-color: rgba(0,0,0,0.6); 24 | color: rgba(0,0,0,0.6); 25 | } 26 | } 27 | 28 | .file-list { 29 | list-style: none; 30 | margin: 0; 31 | padding: 0; 32 | border: 2px solid rgba(0,0,0,0.3); 33 | background: rgba(255,255,255,0.5); 34 | 35 | > li { 36 | position: relative; 37 | padding: 10px; 38 | border-top: 1px solid rgba(0,0,0,0.3); 39 | padding-bottom: 20px; 40 | 41 | &:first-of-type { 42 | border-top: none; 43 | } 44 | 45 | .progress-bar { 46 | position: absolute; 47 | height: 10px; 48 | left: 0; 49 | bottom: 0; 50 | width: 100%; 51 | background: rgba(0,0,0,0.2); 52 | 53 | span { 54 | position: absolute; 55 | height: 100%; 56 | left: 0; 57 | top: 0; 58 | background: green; 59 | } 60 | } 61 | } 62 | 63 | ul { 64 | list-style: none; 65 | margin: 0; 66 | padding: 0; 67 | } 68 | } 69 | } 70 | 71 | #summary { 72 | 73 | .package-info { 74 | list-style: none; 75 | margin: 0; 76 | padding: 0; 77 | } 78 | } 79 | 80 | #result { 81 | 82 | .module-info { 83 | 84 | .package-info { 85 | list-style: none; 86 | margin: 0; 87 | padding: 0; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/app.js: -------------------------------------------------------------------------------- 1 | var angular = require('angular'), 2 | uirouter = require('angular-ui-router'), 3 | 4 | // Angular File Upload (GitHub: https://github.com/nervgh/angular-file-upload/) 5 | fileUpload = require('angular-file-upload'), 6 | 7 | // Doesn't follow convention and return its app name in module.exports. 8 | fileUpload = 'angularFileUpload'; 9 | 10 | var polyCore = require('../../../Core/src/js/app'); 11 | 12 | // Create Angular module. 13 | var angularModule = angular.module('cantarus.poly-deploy.deploy', [ 14 | uirouter, 15 | fileUpload, 16 | polyCore 17 | ]); 18 | 19 | // Values 20 | angularModule.value('appRoot', '/DesktopModules/Cantarus/PolyDeploy/Angular/src/'); 21 | 22 | // Boostrap. 23 | require('./bootstrap')(angularModule); 24 | 25 | module.exports = 'cantarus.poly-deploy.deploy'; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | var controllers = require('./controllers'), 2 | directives = require('./directives'), 3 | services = require('./services'), 4 | config = require('./config'); 5 | 6 | module.exports = function (mod) { 7 | 8 | // Register controllers. 9 | controllers(mod); 10 | 11 | // Register directives. 12 | directives(mod); 13 | 14 | // Register services. 15 | services(mod); 16 | 17 | // Configure. 18 | mod.config(config); 19 | }; 20 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/config.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$stateProvider', '$urlRouterProvider', '$httpProvider', 2 | function ($stateProvider, $urlRouterProvider, $httpProvider) { 3 | 4 | // Default route. 5 | $urlRouterProvider.otherwise('/upload'); 6 | 7 | // States. 8 | $stateProvider 9 | .state('install', { 10 | template: require('./templates/install.html') 11 | }) 12 | .state('install.upload', { 13 | url: '/upload', 14 | template: require('./templates/upload.html'), 15 | controller: 'UploadController' 16 | }) 17 | .state('install.summary', { 18 | template: require('./templates/summary.html'), 19 | controller: 'SummaryController' 20 | }) 21 | .state('install.result', { 22 | template: require('./templates/result.html'), 23 | controller: 'ResultController' 24 | }); 25 | 26 | // Add $http interceptor for DNN Services Framework. 27 | $httpProvider.interceptors.push(['DnnService', 28 | function (DnnService) { 29 | return { 30 | request: function (config) { 31 | 32 | var securityHeaders = DnnService.getSecurityHeaders(); 33 | 34 | Object.keys(securityHeaders).forEach(function (key) { 35 | config.headers[key] = securityHeaders[key]; 36 | }); 37 | 38 | return config; 39 | } 40 | }; 41 | }]); 42 | }]; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/controllers/ResultController.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$scope', '$state','SessionService', 2 | function ($scope, $state, SessionService) { 3 | 4 | // Wait for session. 5 | SessionService.sessionPromise.then( 6 | function (sess) { 7 | 8 | // Add session to scope. 9 | var session = SessionService.session; 10 | 11 | $scope.session = session; 12 | 13 | // Install not started? 14 | if (session.Status === 0) { 15 | 16 | // No, start install. 17 | SessionService.install(); 18 | } 19 | }); 20 | 21 | // Start a new deploy. 22 | $scope.restart = function () { 23 | 24 | // Create a new session. 25 | SessionService.newSession() 26 | .then(function () { 27 | 28 | // Navigate back to beginning. 29 | $state.go('install.upload'); 30 | }); 31 | }; 32 | 33 | // Is the install complete? 34 | $scope.isComplete = function () { 35 | 36 | var session = $scope.session; 37 | 38 | // Is the session complete? 39 | return session 40 | && session.Status 41 | && session.Status === 2; 42 | }; 43 | 44 | // Current status. 45 | $scope.currentStatus = function (session) { 46 | 47 | // Got a response? 48 | if (!session.Response) { 49 | 50 | // No. 51 | return 'Starting install...'; 52 | } 53 | 54 | // Get session response. 55 | var response = session.Response; 56 | 57 | // Get counts. 58 | var installed = 0; 59 | var failed = 0; 60 | 61 | response.forEach(function (modPackage) { 62 | 63 | // Attempted install? 64 | if (modPackage.Attempted) { 65 | 66 | // Success? 67 | modPackage.Success ? installed++ : failed++; 68 | } 69 | }); 70 | 71 | // Create start of string. 72 | var installStatus = 'Installation '; 73 | 74 | // Enumeration for session status. 75 | // Could consider moving this in to the 76 | // SessionDataService and having the correct enumeration 77 | // applied to the data as it comes in. 78 | var sessionStatusEnum = { 79 | '0': 'Not Started', 80 | '1': 'in Progress', 81 | '2': 'Complete' 82 | }; 83 | 84 | // Append status. 85 | installStatus = installStatus + sessionStatusEnum[session.Status.toString()]; 86 | 87 | // Append success and failure. 88 | installStatus = installStatus + ': ' + installed + ' successful installs and ' + failed + ' failures.'; 89 | 90 | return installStatus; 91 | }; 92 | 93 | // Get CSS class to apply to module panel. 94 | $scope.panelStatus = function (modPackage) { 95 | 96 | // Default class. 97 | var panelClass = 'panel-default'; 98 | 99 | // Attempted install? 100 | if (modPackage.Attempted) { 101 | 102 | // Success? 103 | panelClass = modPackage.Success ? 'panel-success' : 'panel-danger'; 104 | } 105 | 106 | return panelClass; 107 | }; 108 | 109 | }]; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/controllers/SummaryController.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$scope', 'SessionService', 2 | function ($scope, SessionService) { 3 | 4 | SessionService.summary().then(function (summaryData) { 5 | console.log(summaryData); 6 | $scope.summaryData = summaryData; 7 | }); 8 | 9 | $scope.dependenciesString = function (deps) { 10 | 11 | var depString = ''; 12 | 13 | angular.forEach(deps, function (dep) { 14 | 15 | if (depString.length !== 0) { 16 | depString = depString + ', '; 17 | } 18 | 19 | depString = depString + dep; 20 | }); 21 | 22 | return depString; 23 | }; 24 | 25 | }]; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/controllers/UploadController.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$scope', 'FileUploader', 'SessionService', 'DnnService', 'apiUrl', 2 | function ($scope, FileUploader, SessionService, DnnService, apiUrl) { 3 | 4 | // Store for errors. 5 | $scope.errors = []; 6 | 7 | // Should we be able to click continue? 8 | $scope.canContinue = function () { 9 | 10 | var canCon = true; 11 | 12 | // Have we uploaded at least one thing and is everything uploaded? 13 | if ($scope.uploader.getNotUploadedItems().length > 0 14 | || $scope.uploader.queue.length < 1) { 15 | 16 | // Can't continue. 17 | canCon = false; 18 | } 19 | 20 | return canCon; 21 | }; 22 | 23 | // Wait for session guid. 24 | SessionService.sessionPromise.then(setupUploader); 25 | 26 | // Setup uploader. 27 | function setupUploader(session) { 28 | 29 | // Construct upload url. 30 | var uploadUrl = apiUrl + 'Session/AddPackage?guid=' + session.Guid; 31 | 32 | // Create uploader. 33 | var uploader = new FileUploader({ 34 | url: uploadUrl, 35 | headers: DnnService.getSecurityHeaders() 36 | }); 37 | 38 | // Place on scope. 39 | $scope.uploader = uploader; 40 | 41 | // Add .zip filter. 42 | uploader.filters.push({ 43 | name: 'zipOnly', 44 | rejectionMessage: 'is not a .zip file.', 45 | fn: function (item, options) { 46 | 47 | // Is there a name? 48 | if (!item.name) { 49 | return false; 50 | } 51 | 52 | // Very rudimentary check to see if there is a .zip extension. 53 | var name = item.name; 54 | 55 | // Get extension. 56 | var ext = name.substring(name.lastIndexOf('.')); 57 | 58 | // Is .zip? 59 | if (ext.toLowerCase() !== '.zip') { 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | }); 66 | 67 | // Add handling for failed file add. 68 | uploader.onWhenAddingFileFailed = function (item, filter, options) { 69 | $scope.errors.push(item.name + ' ' + filter.rejectionMessage); 70 | }; 71 | } 72 | 73 | }]; 74 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/controllers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Controllers 3 | This file will register all your controllers for you. 4 | */ 5 | 6 | // Build require context of controllers. 7 | var controllersContext = require.context('./', true, /\Controller.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each controller. 12 | controllersContext.keys().forEach(function (controller) { 13 | 14 | // Strip path. 15 | var controllerName = controller.substring(controller.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | controllerName = controllerName.substring(0, controllerName.lastIndexOf('.js')); 19 | 20 | // Register controller with module. 21 | mod.controller(controllerName, controllersContext(controller)); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/directives/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Directives 3 | This file will register all your directives for you. 4 | */ 5 | 6 | // Build require context of directives. 7 | var directivesContext = require.context('./', true, /\.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each directive. 12 | directivesContext.keys().forEach(function (directive) { 13 | 14 | // Strip path. 15 | var directiveName = directive.substring(directive.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | directiveName = directiveName.substring(0, directiveName.lastIndexOf('.js')); 19 | 20 | // Lowercase first letter. 21 | directiveName = directiveName.charAt(0).toLowerCase() + directiveName.slice(1); 22 | 23 | // Register directive with module. 24 | mod.directive(directiveName, directivesContext(directive)); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/services/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Services 3 | This file will register all your services for you. 4 | */ 5 | 6 | // Build require context of controllers. 7 | var servicesContext = require.context('./', true, /\Service.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each service. 12 | servicesContext.keys().forEach(function (service) { 13 | 14 | // Strip path. 15 | var serviceName = service.substring(service.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | serviceName = serviceName.substring(0, serviceName.lastIndexOf('.js')); 19 | 20 | // Register service with module. 21 | mod.factory(serviceName, servicesContext(service)); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/templates/install.html: -------------------------------------------------------------------------------- 1 |  2 |
3 |
4 |
5 |
6 | Warning! {{ warning.message }} 7 |
8 |
9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/templates/result.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 |
5 |
6 |

Installing

7 |

8 | Information will refresh automatically, please do not reload the page. 9 |

10 |
11 |
12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 |

{{ currentStatus(session) }}

20 | 21 | 22 | 27 | 28 |
29 | 30 | 31 |
32 |
33 |
34 |

{{ packageZip.Name }}

35 |
36 |
37 |
38 | 39 |
Packages
40 |
41 |
    42 |
  • Name: {{ package.Name }}
  • 43 |
  • Version: {{ package.VersionStr }}
  • 44 |
45 |
46 | 47 |
48 |
Failures
49 |
    50 |
  • 51 | {{ failure }} 52 |
  • 53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | 61 |
62 | 63 |
64 |
65 | 66 | 67 |
-------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/templates/summary.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 |
5 |
6 |

Install Summary

7 |

8 | Below is a summary of the installation if you choose to proceed. 9 |

10 |
11 |
12 | 13 | 14 | 15 |
16 |
17 | 18 |
19 |
20 |

{{ job.order + 1 }}. {{ job.name }}

21 |
22 |
23 | 24 |
Packages
25 |
26 |
    27 |
  • Name: {{ package.name }}
  • 28 |
  • Version: {{ package.version }}
  • 29 |
  • Dependencies: {{ dependenciesString(package.dependencies) }}
  • 30 |
31 |
32 | 33 |
34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 | 47 | 48 |
-------------------------------------------------------------------------------- /PolyDeploy/Clients/Deploy/src/js/templates/upload.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 |
4 | 5 | 6 |
7 |
8 |

Upload

9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 |
17 |
    18 |
  • 19 | {{ errorMessage }} 20 |
  • 21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 |
29 | 30 | 31 |
32 | 33 | 34 |
35 |
36 |
37 |

Drop Files

38 |
39 | Drop files here. 40 |
41 |
42 |
43 |
44 | 45 | 46 | 47 | 55 | 56 | 57 |
58 |
59 |

Controls

60 | 61 | 62 |
63 |
64 | 65 | 66 |
67 | 68 | 69 | 70 |
71 | 72 |

Upload Queue

73 | 74 | 75 |
76 |
77 |
    78 |
  • 79 |
      80 |
    • {{ item.file.name }}

    • 81 |
    • Size: {{ item.file.size / 1024 / 1024 | number: 2 }} MB
    • 82 |
    83 |
    84 | 85 |
    86 |
  • 87 |
88 |
89 |
90 | 91 | 92 |
93 | 94 | 95 |
96 | 97 | 98 |
-------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/Manage.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control language="C#" Inherits="Cantarus.Modules.PolyDeploy.Manage" AutoEventWireup="false" Codebehind="Manage.ascx.cs" %> 2 | 3 | 4 |
5 |
8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/Manage.ascx.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components; 2 | using DotNetNuke.Framework; 3 | using DotNetNuke.Web.Client.ClientResourceManagement; 4 | using System; 5 | 6 | namespace Cantarus.Modules.PolyDeploy 7 | { 8 | public partial class Manage : PolyDeployModuleBase 9 | { 10 | protected override void OnInit(EventArgs e) 11 | { 12 | ClientResourceManager.RegisterStyleSheet(Page, string.Format("{0}/dist/Manage.styles.css", TemplateSourceDirectory)); 13 | 14 | ClientResourceManager.RegisterScript(Page, string.Format("{0}/dist/Manage.bundle.js", TemplateSourceDirectory), 500); 15 | 16 | base.OnInit(e); 17 | } 18 | 19 | protected override void OnLoad(EventArgs e) 20 | { 21 | base.OnLoad(e); 22 | 23 | // Register for Services Framework. 24 | ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/Manage.ascx.designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace Cantarus.Modules.PolyDeploy { 11 | 12 | 13 | public partial class Manage { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/css/main.less: -------------------------------------------------------------------------------- 1 | /* 2 | Manage 3 | Styling for the Manage Angular application. 4 | */ 5 | 6 | // Import from Core. 7 | @import "../../../Core/src/css/main.less"; 8 | 9 | #cantarus-poly-deploy { 10 | 11 | .form-group { 12 | &.form-group-last { 13 | margin-bottom: 0; 14 | } 15 | } 16 | 17 | .can-pagination { 18 | text-align: center; 19 | 20 | ul { 21 | li { 22 | user-select: none; 23 | 24 | a { 25 | cursor: pointer; 26 | width: 45px; 27 | } 28 | } 29 | } 30 | } 31 | 32 | table { 33 | &.table { 34 | tr { 35 | &.critical { 36 | background-color: #f77e7e; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/app.js: -------------------------------------------------------------------------------- 1 | var angular = require('angular'), 2 | uirouter = require('angular-ui-router'), 3 | 4 | // Angular File Upload (GitHub: https://github.com/nervgh/angular-file-upload/) 5 | fileUpload = require('angular-file-upload'), 6 | 7 | // Doesn't follow convention and return its app name in module.exports. 8 | fileUpload = 'angularFileUpload'; 9 | 10 | var polyCore = require('../../../Core/src/js/app'); 11 | 12 | // Create Angular module. 13 | var angularModule = angular.module('cantarus.poly-deploy.manage', [ 14 | uirouter, 15 | fileUpload, 16 | polyCore 17 | ]); 18 | 19 | // Values 20 | angularModule.value('appRoot', '/DesktopModules/Cantarus/PolyDeploy/Angular/src/'); 21 | 22 | // Boostrap. 23 | require('./bootstrap')(angularModule); 24 | 25 | // Configure. 26 | require('./config')(angularModule); 27 | 28 | module.exports = 'cantarus.poly-deploy.manage'; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | var controllers = require('./controllers'), 2 | services = require('./services'); 3 | 4 | module.exports = function (mod) { 5 | 6 | // Register controllers. 7 | controllers(mod); 8 | 9 | // Register services. 10 | services(mod); 11 | }; 12 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (mod) { 2 | mod.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', 3 | function ($stateProvider, $urlRouterProvider, $httpProvider) { 4 | 5 | // Default route. 6 | $urlRouterProvider.otherwise('/events'); 7 | 8 | // States. 9 | $stateProvider 10 | .state('menu', { 11 | template: require('./templates/menu.html') 12 | }) 13 | .state('menu.users', { 14 | url: '/users', 15 | template: require('./templates/users.html'), 16 | controller: 'UsersController' 17 | }) 18 | .state('menu.whitelist', { 19 | url: '/whitelist', 20 | template: require('./templates/whitelist.html'), 21 | controller: 'WhitelistController' 22 | }) 23 | .state('menu.events', { 24 | url: '/events', 25 | template: require('./templates/events.html'), 26 | controller: 'EventsController' 27 | }); 28 | 29 | // Add $http interceptor for DNN Services Framework. 30 | $httpProvider.interceptors.push(['DnnService', 31 | function (DnnService) { 32 | return { 33 | request: function (config) { 34 | 35 | var securityHeaders = DnnService.getSecurityHeaders(); 36 | 37 | Object.keys(securityHeaders).forEach(function (key) { 38 | config.headers[key] = securityHeaders[key]; 39 | }); 40 | 41 | return config; 42 | } 43 | }; 44 | }]); 45 | }]); 46 | }; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/controllers/UsersController.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$scope', 'APIUserDataService', 2 | function ($scope, APIUserDataService) { 3 | 4 | // Load users. 5 | refreshUsers(); 6 | 7 | $scope.newUser = { 8 | name: '', 9 | bypassIPWhitelist: false 10 | }; 11 | 12 | // Create user. 13 | $scope.createUser = function (newUser) { 14 | 15 | // Create the new user and append it, if you call for a refresh 16 | // the API key and encryption keys will be obfuscated. 17 | APIUserDataService.createUser(newUser.name, newUser.bypassIPWhitelist).then( 18 | function (createdUser) { 19 | 20 | // Push on to users. 21 | $scope.users.push(createdUser); 22 | }); 23 | }; 24 | 25 | // Delete user. 26 | $scope.deleteUser = function (apiUser) { 27 | 28 | // Delete the user and then call for a refresh from the server. 29 | APIUserDataService.deleteUser(apiUser).then(refreshUsers); 30 | }; 31 | 32 | // Fetch API users. 33 | function refreshUsers() { 34 | APIUserDataService.getUsers().then( 35 | function (apiUsers) { 36 | 37 | // Pop them on the scope. 38 | $scope.users = apiUsers; 39 | 40 | }); 41 | } 42 | 43 | }]; 44 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/controllers/WhitelistController.js: -------------------------------------------------------------------------------- 1 | module.exports = ['$scope', 'IPSpecDataService', 'SettingDataService', 2 | function ($scope, IPSpecDataService, SettingDataService) { 3 | 4 | $scope.newIp = { 5 | name: '', 6 | ipv4Address: '' 7 | }; 8 | 9 | $scope.errorMessage = null; 10 | 11 | $scope.whitelistStates = [ 12 | { 13 | name: 'Enabled', 14 | value: true 15 | }, 16 | { 17 | name: 'Disabled', 18 | value: false 19 | } 20 | ]; 21 | 22 | $scope.whitelistState = false; 23 | 24 | // Load specs. 25 | refreshSpecs(); 26 | 27 | // Retrieve whitelist state. 28 | SettingDataService.whitelist.getState() 29 | .then(function (setting) { 30 | 31 | // Selected option. 32 | var selected = undefined; 33 | 34 | // Loop options. 35 | angular.forEach($scope.whitelistStates, function (state) { 36 | 37 | // Is this the selected option? 38 | if (state.value === setting) { 39 | selected = state; 40 | } 41 | }); 42 | 43 | // Set on scope. 44 | $scope.whitelistState = selected; 45 | }); 46 | 47 | // Update whitelist state. 48 | $scope.updateWhitelistState = function (state) { 49 | 50 | // Save value. 51 | SettingDataService.whitelist.setState(state.value); 52 | }; 53 | 54 | // Dismiss error message. 55 | $scope.dismissError = function () { 56 | 57 | $scope.errorMessage = null; 58 | }; 59 | 60 | // Create spec. 61 | $scope.createSpec = function (ipSpec) { 62 | 63 | // Create the new spec and append it. 64 | IPSpecDataService.createSpec(ipSpec.name, ipSpec.ipv4Address).then( 65 | function (resp) { 66 | 67 | if (resp.err) { 68 | $scope.errorMessage = resp.err; 69 | return; 70 | } 71 | 72 | // Push on to specs. 73 | $scope.specs.push(resp.ipSpec); 74 | }); 75 | }; 76 | 77 | // Delete spec. 78 | $scope.deleteSpec = function (ipSpec) { 79 | 80 | // Delete the spec and then call for a refresh from the server. 81 | IPSpecDataService.deleteSpec(ipSpec).then(refreshSpecs); 82 | }; 83 | 84 | // Fetch IP specs. 85 | function refreshSpecs() { 86 | IPSpecDataService.getSpecs().then( 87 | function (ipSpecs) { 88 | 89 | // Pop them on the scope. 90 | $scope.specs = ipSpecs; 91 | 92 | }); 93 | } 94 | 95 | }]; -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/controllers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Controllers 3 | This file will register all your controllers for you. 4 | */ 5 | 6 | // Build require context of controllers. 7 | var controllersContext = require.context('./', true, /\Controller.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each controller. 12 | controllersContext.keys().forEach(function (controller) { 13 | 14 | // Strip path. 15 | var controllerName = controller.substring(controller.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | controllerName = controllerName.substring(0, controllerName.lastIndexOf('.js')); 19 | 20 | // Register controller with module. 21 | mod.controller(controllerName, controllersContext(controller)); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/services/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Services 3 | This file will register all your services for you. 4 | */ 5 | 6 | // Build require context of controllers. 7 | var servicesContext = require.context('./', true, /\Service.js$/); 8 | 9 | module.exports = function (mod) { 10 | 11 | // For each service. 12 | servicesContext.keys().forEach(function (service) { 13 | 14 | // Strip path. 15 | var serviceName = service.substring(service.lastIndexOf('/') + 1); 16 | 17 | // Strip extension. 18 | serviceName = serviceName.substring(0, serviceName.lastIndexOf('.js')); 19 | 20 | // Register service with module. 21 | mod.factory(serviceName, servicesContext(service)); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/templates/events.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 |
5 | 6 | 7 |
8 |
9 |
10 |

Event Logs

11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 |
DateTypeMessage
{{ eventLog.Date }}{{ eventLog.EventType }}{{ eventLog.Message }}
30 | 31 | 32 |
33 |
34 | 57 |
58 |
59 | 60 |
61 |
62 |
63 | 64 | 65 |
66 | 67 | 68 |
-------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/templates/menu.html: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /PolyDeploy/Clients/Manage/src/js/templates/users.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 |
5 | 6 | 7 |
8 |
9 |
10 |

New API User

11 |
12 |
13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | 38 | 39 | 40 |
41 |
42 |
43 |

API Users

44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
NameAPI KeyEncryption KeyBypass IP Allow List
{{ user.Name }}{{ user.APIKey }}{{ user.EncryptionKey }}{{ user.BypassIPWhitelist }}
66 |
67 |
68 |
69 | 70 | 71 |
72 | 73 | 74 |
75 | -------------------------------------------------------------------------------- /PolyDeploy/Components/APIUserManager.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers; 2 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components 7 | { 8 | internal static class APIUserManager 9 | { 10 | private static APIUserDataController APIUserDC = new APIUserDataController(); 11 | 12 | /// 13 | /// Creates a new APIUser with the passed name. 14 | /// 15 | /// 16 | /// 17 | public static APIUser Create(string name) 18 | { 19 | return Create(name, bypass: false); 20 | } 21 | 22 | public static APIUser Create(string name, bool bypass) 23 | { 24 | APIUser newApiUser; 25 | 26 | try 27 | { 28 | newApiUser = new APIUser(name, bypass); 29 | 30 | APIUserDC.Create(newApiUser); 31 | } 32 | catch (Exception ex) 33 | { 34 | return null; 35 | } 36 | 37 | return newApiUser; 38 | } 39 | 40 | /// 41 | /// Gets all APIUsers. 42 | /// 43 | /// 44 | public static IEnumerable GetAll() 45 | { 46 | return APIUserDC.Get(); 47 | } 48 | 49 | /// 50 | /// Retrieves a single APIUser by its id. 51 | /// 52 | /// 53 | /// 54 | public static APIUser GetById(int id) 55 | { 56 | return APIUserDC.Get(id); 57 | } 58 | 59 | /// 60 | /// Retrieves a single APIUser by its api key. 61 | /// 62 | /// 63 | /// 64 | public static APIUser GetByAPIKey(string apiKey) 65 | { 66 | return APIUserDC.Get(apiKey); 67 | } 68 | 69 | /// 70 | /// Updates the passed APIUser. 71 | /// 72 | /// 73 | /// 74 | public static APIUser Update(APIUser apiUser) 75 | { 76 | APIUserDC.Update(apiUser); 77 | 78 | return APIUserDC.Get(apiUser.APIUserId); 79 | } 80 | 81 | /// 82 | /// Deletes the passed APIUser. 83 | /// 84 | /// 85 | public static void Delete(APIUser apiUser) 86 | { 87 | APIUserDC.Delete(apiUser); 88 | } 89 | 90 | /// 91 | /// Looks up an APIUser by its api key and prepares it for use. 92 | /// 93 | /// 94 | /// 95 | public static APIUser FindAndPrepare(string apiKey) 96 | { 97 | // Lookup user by api key. 98 | APIUser apiUser = APIUserDC.Get(apiKey); 99 | 100 | // Verify and prepare for use. 101 | if (apiUser != null && apiUser.PrepareForUse(apiKey)) 102 | { 103 | // Return api user. 104 | return apiUser; 105 | } 106 | 107 | // Didn't find api user or preparation failed. 108 | return null; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/DataControllers/APIUserDataController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using DotNetNuke.Data; 3 | using System.Collections.Generic; 4 | 5 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers 6 | { 7 | internal class APIUserDataController 8 | { 9 | #region Create 10 | 11 | public void Create(APIUser apiUser) 12 | { 13 | using (IDataContext context = DataContext.Instance()) 14 | { 15 | var repo = context.GetRepository(); 16 | 17 | repo.Insert(apiUser); 18 | } 19 | } 20 | 21 | #endregion 22 | 23 | #region Read 24 | 25 | public IEnumerable Get() 26 | { 27 | using (IDataContext context = DataContext.Instance()) 28 | { 29 | var repo = context.GetRepository(); 30 | 31 | return repo.Get(); 32 | } 33 | } 34 | 35 | public APIUser Get(int apiUserId) 36 | { 37 | using (IDataContext context = DataContext.Instance()) 38 | { 39 | var repo = context.GetRepository(); 40 | 41 | return repo.GetById(apiUserId); 42 | } 43 | } 44 | 45 | public APIUser Get(string apiKey) 46 | { 47 | using (IDataContext context = DataContext.Instance()) 48 | { 49 | return context.ExecuteSingleOrDefault(System.Data.CommandType.StoredProcedure, "{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUserByAPIKey]", apiKey); 50 | } 51 | } 52 | 53 | #endregion 54 | 55 | #region Update 56 | 57 | public void Update(APIUser apiUser) 58 | { 59 | using (IDataContext context = DataContext.Instance()) 60 | { 61 | var repo = context.GetRepository(); 62 | 63 | repo.Update(apiUser); 64 | } 65 | } 66 | 67 | #endregion 68 | 69 | #region Delete 70 | 71 | public void Delete(APIUser apiUser) 72 | { 73 | using (IDataContext context = DataContext.Instance()) 74 | { 75 | var repo = context.GetRepository(); 76 | 77 | repo.Delete(apiUser); 78 | } 79 | } 80 | 81 | #endregion 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/DataControllers/EventLogDataController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using Cantarus.Modules.PolyDeploy.Components.Logging; 3 | using DotNetNuke.Data; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers 9 | { 10 | internal class EventLogDataController 11 | { 12 | #region Create 13 | 14 | public void Create(EventLog eventLog) 15 | { 16 | using (IDataContext context = DataContext.Instance()) 17 | { 18 | var repo = context.GetRepository(); 19 | 20 | eventLog.Date = DateTime.Now; 21 | 22 | repo.Insert(eventLog); 23 | } 24 | } 25 | 26 | #endregion 27 | 28 | #region Read 29 | 30 | public IEnumerable Get() 31 | { 32 | using (IDataContext context = DataContext.Instance()) 33 | { 34 | var repo = context.GetRepository(); 35 | 36 | return repo.Get(); 37 | } 38 | } 39 | 40 | public IEnumerable Browse(int pageIndex, int pageSize, string eventType, EventLogSeverity? severity) 41 | { 42 | using (IDataContext context = DataContext.Instance()) 43 | { 44 | return context.ExecuteQuery( 45 | System.Data.CommandType.StoredProcedure, 46 | "{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_GetEventLogsPage]", 47 | pageIndex, 48 | pageSize, 49 | eventType, 50 | severity 51 | ); 52 | } 53 | } 54 | 55 | public int BrowseCount(int pageIndex, int pageSize, string eventType, EventLogSeverity? severity) 56 | { 57 | using (IDataContext context = DataContext.Instance()) 58 | { 59 | return context.ExecuteQuery( 60 | System.Data.CommandType.StoredProcedure, 61 | "{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_GetEventLogsPageTotal]", 62 | pageIndex, 63 | pageSize, 64 | eventType, 65 | severity 66 | ).FirstOrDefault(); 67 | } 68 | } 69 | 70 | public IEnumerable GetEventTypes() 71 | { 72 | using (IDataContext context = DataContext.Instance()) 73 | { 74 | return context.ExecuteQuery(System.Data.CommandType.Text, "SELECT DISTINCT [EventType] FROM [dbo].[Cantarus_PolyDeploy_EventLogs]", null); 75 | } 76 | } 77 | 78 | public int EventCount() 79 | { 80 | using (IDataContext context = DataContext.Instance()) 81 | { 82 | return context.ExecuteQuery(System.Data.CommandType.Text, "SELECT COUNT(*) FROM [dbo].[Cantarus_PolyDeploy_EventLogs]", null).FirstOrDefault(); 83 | } 84 | } 85 | 86 | #endregion 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/DataControllers/IPSpecDataController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using DotNetNuke.Data; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers 7 | { 8 | internal class IPSpecDataController 9 | { 10 | #region Create 11 | 12 | public void Create(IPSpec ipSpec) 13 | { 14 | using (IDataContext context = DataContext.Instance()) 15 | { 16 | var repo = context.GetRepository(); 17 | 18 | repo.Insert(ipSpec); 19 | } 20 | } 21 | 22 | #endregion 23 | 24 | #region Read 25 | 26 | public IEnumerable Get() 27 | { 28 | using (IDataContext context = DataContext.Instance()) 29 | { 30 | var repo = context.GetRepository(); 31 | 32 | return repo.Get(); 33 | } 34 | } 35 | 36 | public IPSpec Get(int ipSpecId) 37 | { 38 | using (IDataContext context = DataContext.Instance()) 39 | { 40 | var repo = context.GetRepository(); 41 | 42 | return repo.GetById(ipSpecId); 43 | } 44 | } 45 | 46 | public IPSpec Get(string address) 47 | { 48 | using (IDataContext context = DataContext.Instance()) 49 | { 50 | return context.ExecuteSingleOrDefault( 51 | System.Data.CommandType.StoredProcedure, 52 | "{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_IPSpecByAddress]", 53 | address 54 | ); 55 | } 56 | } 57 | 58 | public IPSpec GetByName(string name) 59 | { 60 | using (IDataContext context = DataContext.Instance()) 61 | { 62 | var repo = context.GetRepository(); 63 | 64 | return repo.Find("WHERE [Name] = @0", name).FirstOrDefault(); 65 | } 66 | } 67 | 68 | #endregion 69 | 70 | #region Update 71 | 72 | // N/A 73 | 74 | #endregion 75 | 76 | #region Delete 77 | 78 | public void Delete(IPSpec ipSpec) 79 | { 80 | using (IDataContext context = DataContext.Instance()) 81 | { 82 | var repo = context.GetRepository(); 83 | 84 | repo.Delete(ipSpec); 85 | } 86 | } 87 | 88 | #endregion 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/DataControllers/SessionDataController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using DotNetNuke.Data; 3 | using System.Linq; 4 | 5 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers 6 | { 7 | internal class SessionDataController 8 | { 9 | public Session FindByGuid(string guid) 10 | { 11 | using (IDataContext context = DataContext.Instance()) 12 | { 13 | var repo = context.GetRepository(); 14 | 15 | return repo.Find("WHERE Guid = @0", guid).FirstOrDefault(); 16 | } 17 | } 18 | 19 | public void Create(Session session) 20 | { 21 | using (IDataContext context = DataContext.Instance()) 22 | { 23 | var repo = context.GetRepository(); 24 | 25 | repo.Insert(session); 26 | } 27 | } 28 | 29 | public void Update(Session session) 30 | { 31 | using (IDataContext context = DataContext.Instance()) 32 | { 33 | var repo = context.GetRepository(); 34 | 35 | repo.Update(session); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/DataControllers/SettingDataController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using DotNetNuke.Data; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers 7 | { 8 | internal class SettingDataController 9 | { 10 | /// 11 | /// Retrieve a setting from the database by its group and key. 12 | /// 13 | /// 14 | /// 15 | /// 16 | public Setting GetSetting(string group, string key) 17 | { 18 | using (IDataContext context = DataContext.Instance()) 19 | { 20 | var repo = context.GetRepository(); 21 | 22 | return repo.Find("WHERE [Group] = @0 AND [Key] = @1", group, key).FirstOrDefault(); 23 | } 24 | } 25 | 26 | /// 27 | /// Return all settings belonging to a group. 28 | /// 29 | /// 30 | /// 31 | public IEnumerable GetSettings(string group) 32 | { 33 | using (IDataContext context = DataContext.Instance()) 34 | { 35 | var repo = context.GetRepository(); 36 | 37 | return repo.Find("WHERE [Group] = @0", group); 38 | } 39 | } 40 | 41 | /// 42 | /// Create a new setting. 43 | /// 44 | /// 45 | public void Create(Setting setting) 46 | { 47 | using (IDataContext context = DataContext.Instance()) 48 | { 49 | var repo = context.GetRepository(); 50 | 51 | repo.Insert(setting); 52 | } 53 | } 54 | 55 | /// 56 | /// Update an existing setting. 57 | /// 58 | /// 59 | public void Update(Setting setting) 60 | { 61 | using (IDataContext context = DataContext.Instance()) 62 | { 63 | var repo = context.GetRepository(); 64 | 65 | repo.Update(setting); 66 | } 67 | } 68 | 69 | /// 70 | /// Delete a setting. 71 | /// 72 | /// 73 | public void Delete(Setting setting) 74 | { 75 | using (IDataContext context = DataContext.Instance()) 76 | { 77 | var repo = context.GetRepository(); 78 | 79 | repo.Delete(setting); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/Models/EventLog.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.Logging; 2 | using DotNetNuke.ComponentModel.DataAnnotations; 3 | using System; 4 | 5 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.Models 6 | { 7 | [TableName("Cantarus_PolyDeploy_EventLogs")] 8 | [PrimaryKey("EventLogID")] 9 | public class EventLog 10 | { 11 | public int EventLogID { get; set; } 12 | public DateTime Date { get; set; } 13 | private string _EventType; 14 | public string EventType { 15 | get 16 | { 17 | if (!string.IsNullOrEmpty(_EventType)) 18 | { 19 | return _EventType.ToUpper(); 20 | } 21 | 22 | return _EventType; 23 | } 24 | 25 | set 26 | { 27 | _EventType = value.ToUpper(); 28 | } 29 | } 30 | public EventLogSeverity Severity { get; set; } 31 | public string Message { get; set; } 32 | public string StackTrace { get; set; } 33 | 34 | public EventLog() { } 35 | 36 | public EventLog(string eventType, EventLogSeverity severity, string message) 37 | { 38 | EventType = eventType; 39 | Severity = severity; 40 | Message = message; 41 | } 42 | 43 | public EventLog(string eventType, EventLogSeverity severity, Exception ex) 44 | { 45 | EventType = eventType; 46 | Severity = severity; 47 | Message = ex.Message; 48 | StackTrace = ex.StackTrace; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/Models/IPSpec.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.ComponentModel.DataAnnotations; 2 | using System; 3 | 4 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.Models 5 | { 6 | [TableName("Cantarus_PolyDeploy_IPSpecs")] 7 | [PrimaryKey("IPSpecID")] 8 | public class IPSpec : Obfuscated 9 | { 10 | /// 11 | /// Integer ID of IPSpec. 12 | /// 13 | public int IPSpecId { get; set; } 14 | 15 | /// 16 | /// Name used to identify this address. 17 | /// 18 | public string Name { get; set; } 19 | 20 | /// 21 | /// Salted and hashed address. 22 | /// 23 | public string Address_Sha { get; set; } 24 | 25 | /// 26 | /// Randomly generated and used when hashing the address. 27 | /// 28 | public string Salt { get; set; } 29 | 30 | /// 31 | /// Address in plain text. 32 | /// 33 | [IgnoreColumn] 34 | public string Address { get; } 35 | 36 | /// 37 | /// Basic constructor, without it PetaPoco will throw an exception. 38 | /// 39 | public IPSpec () 40 | { 41 | Address = "********************************"; 42 | } 43 | 44 | /// 45 | /// Constructor for creating a new IPSpec. 46 | /// 47 | /// 48 | public IPSpec (string name, string address) : this() 49 | { 50 | if (string.IsNullOrEmpty(name)) 51 | { 52 | throw new Exception("Unable to create new IPSpec without a name."); 53 | } 54 | 55 | if (string.IsNullOrEmpty(address)) 56 | { 57 | throw new Exception("Unable to create new IPSpec without an address."); 58 | } 59 | 60 | Name = name; 61 | Address = address; 62 | 63 | // Generate salt. 64 | Salt = GenerateSalt(); 65 | 66 | // Hash address with salt. 67 | Address_Sha = GenerateHash(address, Salt); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/Models/Obfuscated.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Libraries.Encryption; 2 | 3 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.Models 4 | { 5 | public class Obfuscated 6 | { 7 | internal static string GenerateHash(string value, string salt) 8 | { 9 | // Hash. 10 | string hash = CryptoUtilities.SHA256HashString(value + salt); 11 | 12 | // Return upper case. 13 | return hash.ToUpper(); 14 | } 15 | 16 | internal static string GenerateSalt() 17 | { 18 | // Salt length of 16 bytes should be fine for now. 19 | int saltLength = 16; 20 | 21 | // Generate random bytes. 22 | byte[] bytes = CryptoUtilities.GenerateRandomBytes(saltLength); 23 | 24 | // Convert to string. 25 | string salt = ""; 26 | 27 | for (int i = 0; i < bytes.Length; i++) 28 | { 29 | salt = string.Format("{0}{1:X2}", salt, bytes[i]); 30 | } 31 | 32 | // Return upper case. 33 | return salt.ToUpper(); 34 | } 35 | 36 | protected Obfuscated() { } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/Models/Session.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.ComponentModel.DataAnnotations; 2 | using System; 3 | 4 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.Models 5 | { 6 | public enum SessionStatus 7 | { 8 | NotStarted = 0, 9 | InProgess = 1, 10 | Complete = 2 11 | } 12 | 13 | [TableName("Cantarus_PolyDeploy_Sessions")] 14 | [PrimaryKey("SessionID")] 15 | public class Session 16 | { 17 | public int SessionID { get; set; } 18 | public string Guid { get; set; } 19 | public SessionStatus Status { get; set; } 20 | public string Response { get; set; } 21 | public DateTime LastUsed { get; set; } 22 | 23 | public Session() { } 24 | 25 | public Session(string guid) 26 | { 27 | Guid = guid; 28 | Status = SessionStatus.NotStarted; 29 | LastUsed = DateTime.Now; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /PolyDeploy/Components/DataAccess/Models/Setting.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.ComponentModel.DataAnnotations; 2 | 3 | namespace Cantarus.Modules.PolyDeploy.Components.DataAccess.Models 4 | { 5 | [TableName("Cantarus_PolyDeploy_Settings")] 6 | [PrimaryKey("SettingID")] 7 | public class Setting 8 | { 9 | public int SettingId { get; set; } 10 | public string Group { get; set; } 11 | public string Key { get; set; } 12 | public string Value { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PolyDeploy/Components/Exceptions/IPSpecExistsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Cantarus.Modules.PolyDeploy.Components.Exceptions 5 | { 6 | [Serializable] 7 | public class IPSpecExistsException : Exception 8 | { 9 | public IPSpecExistsException() { } 10 | 11 | public IPSpecExistsException(string message) 12 | : base(message) { } 13 | 14 | public IPSpecExistsException(string message, Exception innerException) 15 | : base(message, innerException) { } 16 | 17 | public IPSpecExistsException(SerializationInfo info, StreamingContext context) 18 | : base(info, context) { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /PolyDeploy/Components/Exceptions/SettingNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Cantarus.Modules.PolyDeploy.Components.Exceptions 5 | { 6 | [Serializable] 7 | public class SettingNotFoundException : Exception 8 | { 9 | public static SettingNotFoundException Create(string group, string key) 10 | { 11 | return new SettingNotFoundException($"Setting in group '{group}' with key '{key}' was not found."); 12 | } 13 | 14 | public SettingNotFoundException() { } 15 | 16 | public SettingNotFoundException(string message) 17 | : base(message) { } 18 | 19 | public SettingNotFoundException(string message, Exception innerException) 20 | : base(message, innerException) { } 21 | 22 | public SettingNotFoundException(SerializationInfo info, StreamingContext context) 23 | : base(info, context) { } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /PolyDeploy/Components/IPSpecManager.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers; 2 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 3 | using Cantarus.Modules.PolyDeploy.Components.Exceptions; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Net; 7 | 8 | namespace Cantarus.Modules.PolyDeploy.Components 9 | { 10 | internal static class IPSpecManager 11 | { 12 | private static IPSpecDataController IPSpecDC = new IPSpecDataController(); 13 | 14 | /// 15 | /// Create a new IPSpec object using the passed name and address. 16 | /// 17 | /// 18 | /// 19 | /// 20 | public static IPSpec Create(string name, string address) 21 | { 22 | IPSpec ipSpec = IPSpecDC.GetByName(name); 23 | 24 | if (ipSpec != null) 25 | { 26 | throw new IPSpecExistsException($"An entry named '{ipSpec.Name}' already exists."); 27 | } 28 | 29 | ipSpec = IPSpecDC.Get(address); 30 | 31 | if (ipSpec != null) 32 | { 33 | throw new IPSpecExistsException($"IP '{address}' is already whitelisted by entry named '{ipSpec.Name}'."); 34 | } 35 | 36 | ipSpec = new IPSpec(name, address); 37 | 38 | IPSpecDC.Create(ipSpec); 39 | 40 | return ipSpec; 41 | } 42 | 43 | /// 44 | /// Retrieve all the IPSpec objects from the database. 45 | /// 46 | /// 47 | public static IEnumerable GetAll() 48 | { 49 | return IPSpecDC.Get(); 50 | } 51 | 52 | /// 53 | /// Get single IPSpec by its id. 54 | /// 55 | /// 56 | /// 57 | public static IPSpec GetById(int id) 58 | { 59 | return IPSpecDC.Get(id); 60 | } 61 | 62 | /// 63 | /// Check to see if the passed address is whitelisted. 64 | /// 65 | /// 66 | /// 67 | public static bool IsWhitelisted(string address) 68 | { 69 | if (!IPAddress.TryParse(address, out _)) 70 | { 71 | // see if address is an IP plus port, e.g. "1.1.1.1:58290" 72 | if (Uri.TryCreate(Uri.UriSchemeHttps + Uri.SchemeDelimiter + address, UriKind.Absolute, out Uri uri)) 73 | { 74 | if (uri.HostNameType != UriHostNameType.IPv4 && uri.HostNameType != UriHostNameType.IPv6) 75 | { 76 | return false; 77 | } 78 | 79 | address = uri.Host; 80 | } 81 | } 82 | 83 | IPSpec ipSpec = IPSpecDC.Get(address); 84 | 85 | return ipSpec != null; 86 | } 87 | 88 | /// 89 | /// Delete the passed IPSpec. 90 | /// 91 | /// 92 | public static void Delete(IPSpec ipSpec) 93 | { 94 | IPSpecDC.Delete(ipSpec); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /PolyDeploy/Components/Logging/EventLogManager.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers; 2 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components.Logging 7 | { 8 | internal static class EventLogManager 9 | { 10 | private static EventLogDataController LogDC = new EventLogDataController(); 11 | 12 | public static IEnumerable Browse(int pageIndex, int pageSize, string eventType, EventLogSeverity? severity) 13 | { 14 | return LogDC.Browse(pageIndex, pageSize, eventType, severity); 15 | } 16 | 17 | public static int BrowseCount(int pageIndex, int pageSize, string eventType, EventLogSeverity? severity) 18 | { 19 | return LogDC.BrowseCount(pageIndex, pageSize, eventType, severity); 20 | } 21 | 22 | public static IEnumerable GetEventTypes() 23 | { 24 | return LogDC.GetEventTypes(); 25 | } 26 | 27 | public static int EventCount() 28 | { 29 | return LogDC.EventCount(); 30 | } 31 | 32 | public static void Log(string eventType, EventLogSeverity severity, string message = null, Exception ex = null) 33 | { 34 | // TODO: Internal logging switched on? 35 | LogInternal(eventType, severity, message, ex); 36 | 37 | // TODO: DNN logging switched on? 38 | // Log to DNN event log. 39 | } 40 | 41 | private static void LogInternal(string eventType, EventLogSeverity severity, string message, Exception ex) 42 | { 43 | EventLog eventLog; 44 | 45 | if (ex != null) 46 | { 47 | eventLog = new EventLog(eventType, severity, ex); 48 | } 49 | else 50 | { 51 | eventLog = new EventLog(eventType, severity, message); 52 | } 53 | 54 | LogDC.Create(eventLog); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PolyDeploy/Components/Logging/EventLogSeverity.cs: -------------------------------------------------------------------------------- 1 | namespace Cantarus.Modules.PolyDeploy.Components.Logging 2 | { 3 | public enum EventLogSeverity 4 | { 5 | Info = 0, 6 | Warning = 1, 7 | Alert = 2, 8 | Critical = 3 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /PolyDeploy/Components/PackageDependency.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Services.Installer.Dependencies; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Xml.XPath; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components 7 | { 8 | internal class PackageDependency 9 | { 10 | private static readonly ISet PackageTypes = new HashSet(new [] { "PACKAGE", "MANAGEDPACKAGE" }, StringComparer.OrdinalIgnoreCase); 11 | 12 | public bool IsPackageDependency { get; set; } 13 | public string PackageName { get; set; } 14 | public string DependencyVersion { get; set; } 15 | internal bool DnnMet { get; set; } 16 | internal bool DeployMet { get; set; } 17 | 18 | internal bool IsMet 19 | { 20 | get 21 | { 22 | return DnnMet || DeployMet; 23 | } 24 | } 25 | 26 | public PackageDependency(XPathNavigator dependencyRoot) 27 | { 28 | IsPackageDependency = PackageTypes.Contains(dependencyRoot.GetAttribute("type", "")); 29 | PackageName = dependencyRoot.Value; 30 | DependencyVersion = dependencyRoot.GetAttribute("version", ""); 31 | DnnMet = false; 32 | DeployMet = false; 33 | 34 | IDependency dep = DependencyFactory.GetDependency(dependencyRoot); 35 | 36 | DnnMet = dep.IsValid; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PolyDeploy/Components/PackageJob.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Services.Installer.Installers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Xml.XPath; 6 | 7 | namespace Cantarus.Modules.PolyDeploy.Components 8 | { 9 | internal class PackageJob 10 | { 11 | public string Name { get; set; } 12 | public List Dependencies { get; set; } 13 | 14 | public string VersionStr 15 | { 16 | get 17 | { 18 | return Version.ToString(); 19 | } 20 | } 21 | 22 | public bool CanInstall 23 | { 24 | get 25 | { 26 | foreach (PackageDependency dependency in Dependencies) 27 | { 28 | if (!dependency.IsMet) 29 | { 30 | return false; 31 | } 32 | } 33 | 34 | return true; 35 | } 36 | } 37 | 38 | private Version Version { get; set; } 39 | 40 | public PackageJob(PackageInstaller packageInstaller) 41 | { 42 | Name = packageInstaller.Package.Name; 43 | Version = packageInstaller.Package.Version; 44 | Dependencies = new List(); 45 | 46 | XPathDocument document = new XPathDocument(new StringReader(packageInstaller.Package.Manifest)); 47 | 48 | XPathNavigator rootNav = document.CreateNavigator(); 49 | 50 | rootNav.MoveToFirstChild(); 51 | 52 | foreach (XPathNavigator nav in rootNav.Select("dependencies/dependency")) 53 | { 54 | Dependencies.Add(new PackageDependency(nav)); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /PolyDeploy/Components/PolyDeployModuleBase.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Entities.Modules; 2 | 3 | namespace Cantarus.Modules.PolyDeploy.Components 4 | { 5 | public class PolyDeployModuleBase : PortalModuleBase 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /PolyDeploy/Components/PolyDeploySettingsBase.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Entities.Modules; 2 | 3 | namespace Cantarus.Modules.PolyDeploy.Components 4 | { 5 | public class PolyDeploySettingsBase : ModuleSettingsBase 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /PolyDeploy/Components/RemoteDeployment.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using DotNetNuke.Services.Log.EventLog; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components 7 | { 8 | internal class RemoteDeployment : Deployment 9 | { 10 | private APIUser APIUser { get; set; } 11 | 12 | public RemoteDeployment(Session session, string ipAddress, string apiKey) : base(session, ipAddress) 13 | { 14 | // Retrieve our API user. 15 | APIUser = APIUserManager.GetByAPIKey(apiKey); 16 | 17 | // Did we find an API user? 18 | if (APIUser == null) 19 | { 20 | throw new Exception("API user not found, cannot continue. Shouldn't have been able to get here."); 21 | } 22 | } 23 | 24 | protected override void LogAnyFailures(List jobs) 25 | { 26 | EventLogController elc = new EventLogController(); 27 | 28 | foreach (InstallJob job in jobs) 29 | { 30 | foreach (string failure in job.Failures) 31 | { 32 | string log = string.Format("(IP: {0} | APIUserID: {1}) {2}", IPAddress, APIUser.APIUserId, failure); 33 | 34 | elc.AddLog("PolyDeploy", log, EventLogController.EventLogType.HOST_ALERT); 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PolyDeploy/Components/SessionManager.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers; 2 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 3 | using System; 4 | using System.IO; 5 | 6 | namespace Cantarus.Modules.PolyDeploy.Components 7 | { 8 | internal static class SessionManager 9 | { 10 | private static SessionDataController SessionDC = new SessionDataController(); 11 | 12 | public static Session CreateSession() 13 | { 14 | string directory = new DirectoryInfo(AvailableSessionDirectory()).Name; 15 | 16 | Session session = new Session(directory); 17 | 18 | SessionDC.Create(session); 19 | 20 | return session; 21 | } 22 | 23 | public static Session GetSession(string sessionGuid) 24 | { 25 | return SessionDC.FindByGuid(sessionGuid); ; 26 | } 27 | 28 | public static bool SessionExists(string sessionGuid) 29 | { 30 | Session session = SessionDC.FindByGuid(sessionGuid); 31 | 32 | if (session == null) 33 | { 34 | return false; 35 | } 36 | 37 | session.LastUsed = DateTime.Now; 38 | 39 | SessionDC.Update(session); 40 | 41 | return true; 42 | } 43 | 44 | public static void AddPackage(string sessionGuid, Stream packageStream, string filename) 45 | { 46 | Session session = SessionDC.FindByGuid(sessionGuid); 47 | 48 | if (session == null) 49 | { 50 | throw new Exception(string.Format("No session exists with guid: {0}", sessionGuid)); 51 | } 52 | 53 | using (FileStream fs = File.Create(Path.Combine(Utilities.ModulePath, "Sessions", session.Guid, filename))) 54 | { 55 | packageStream.CopyTo(fs); 56 | } 57 | } 58 | 59 | public static string PathForSession (string sessionGuid) 60 | { 61 | Session session = SessionDC.FindByGuid(sessionGuid); 62 | 63 | if (session == null) 64 | { 65 | throw new Exception(string.Format("No session exists with guid: {0}", sessionGuid)); 66 | } 67 | 68 | return Path.Combine(Utilities.ModulePath, "Sessions", session.Guid); 69 | } 70 | 71 | private static string AvailableSessionDirectory() 72 | { 73 | // Start with the module root. 74 | string basePath = Utilities.ModulePath; 75 | 76 | // Check we found the module directory. 77 | if (Directory.Exists(basePath)) 78 | { 79 | // Prepare a sessions directory. 80 | basePath = Path.Combine(basePath, "Sessions"); 81 | 82 | // Does it exist? 83 | if (!Directory.Exists(basePath)) 84 | { 85 | // No, create it. 86 | Directory.CreateDirectory(basePath); 87 | } 88 | } 89 | 90 | // Generate a random folder in the desired path. 91 | string dir = Path.Combine(basePath, Utilities.RandomName()); 92 | 93 | // Does it already exist? I doubt it. 94 | if (Directory.Exists(dir)) 95 | { 96 | // My mistake, try again! 97 | return AvailableSessionDirectory(); 98 | } 99 | 100 | // Create the folder. 101 | Directory.CreateDirectory(dir); 102 | 103 | return dir; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /PolyDeploy/Components/SettingManager.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.DataControllers; 2 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 3 | using Cantarus.Modules.PolyDeploy.Components.Exceptions; 4 | using DotNetNuke.Common.Utilities; 5 | using System; 6 | 7 | namespace Cantarus.Modules.PolyDeploy.Components 8 | { 9 | internal class SettingManager 10 | { 11 | private const string SettingCacheKey = "Cantarus:PolyDeploy:Setting_"; 12 | 13 | private static SettingDataController SettingDC = new SettingDataController(); 14 | 15 | public static Setting GetSetting(string group, string key) 16 | { 17 | Setting setting; 18 | 19 | // Attempt to retrieve from cache. 20 | string cacheKey = BuildCacheKey(group, key); 21 | 22 | setting = DataCache.GetCache(cacheKey); 23 | 24 | // Was in cache? 25 | if (setting == null) 26 | { 27 | // Not in cache, go to database. 28 | setting = SettingDC.GetSetting(group, key); 29 | 30 | // Was in db? 31 | if (setting != null) 32 | { 33 | // Cache it for 15 minutes. 34 | DataCache.SetCache(cacheKey, setting, TimeSpan.FromMinutes(15)); 35 | } 36 | else 37 | { 38 | throw SettingNotFoundException.Create(group, key); 39 | } 40 | } 41 | 42 | return setting; 43 | } 44 | 45 | public static void SetSetting(string group, string key, string value) 46 | { 47 | // Retrieve setting. 48 | Setting setting = SettingDC.GetSetting(group, key); 49 | 50 | // Does it already exist? 51 | if (setting == null) 52 | { 53 | // No, create it. 54 | setting = new Setting() 55 | { 56 | Group = group, 57 | Key = key, 58 | Value = value 59 | }; 60 | 61 | SettingDC.Create(setting); 62 | } 63 | else 64 | { 65 | // Yes, Update it. 66 | setting.Value = value; 67 | 68 | SettingDC.Update(setting); 69 | } 70 | 71 | // Clear cache. 72 | DataCache.RemoveCache(BuildCacheKey(group, key)); 73 | } 74 | 75 | private static string BuildCacheKey(string group, string key) 76 | { 77 | return $"{SettingCacheKey}{group}_{key}"; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /PolyDeploy/Components/Utilities.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Common; 2 | using System; 3 | using System.IO; 4 | 5 | namespace Cantarus.Modules.PolyDeploy.Components 6 | { 7 | internal class Utilities 8 | { 9 | public static string ModulePath = Path.Combine(Globals.ApplicationMapPath, "DesktopModules", "Cantarus", "PolyDeploy"); 10 | 11 | public static string AvailableTempDirectory(string basePath = null) 12 | { 13 | // Need to set sensible base? 14 | if (basePath == null) 15 | { 16 | // We'll create a temporary folder in the module folder. 17 | basePath = ModulePath; 18 | 19 | // Check we found the module directory. 20 | if (Directory.Exists(basePath)) 21 | { 22 | // Prepare a temporary directory. 23 | basePath = Path.Combine(basePath, "Temp"); 24 | 25 | // Does it exist? 26 | if (!Directory.Exists(basePath)) 27 | { 28 | // No, create it. 29 | Directory.CreateDirectory(basePath); 30 | } 31 | } 32 | else 33 | { 34 | // No module directory, use windows temp. 35 | basePath = Path.GetTempPath(); 36 | } 37 | } 38 | 39 | // Generate a random folder in the desired path. 40 | string dir = Path.Combine(basePath, "tmp-" + RandomName()); 41 | 42 | // Does it already exist? I doubt it. 43 | if (Directory.Exists(dir)) 44 | { 45 | // My mistake, try again! 46 | return AvailableTempDirectory(); 47 | } 48 | 49 | return dir; 50 | } 51 | 52 | public static string RandomName() 53 | { 54 | // Get new guid as string. 55 | string guidString = Guid.NewGuid().ToString(); 56 | 57 | // Remove hyphens, uppercase and return. 58 | return guidString.Replace("-", null).ToUpper(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /PolyDeploy/Components/WebAPI/ActionFilters/APIAuthentication.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using Cantarus.Modules.PolyDeploy.Components.Logging; 3 | using System; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Web.Http.Controllers; 7 | using System.Web.Http.Filters; 8 | 9 | namespace Cantarus.Modules.PolyDeploy.Components.WebAPI.ActionFilters 10 | { 11 | internal class APIAuthentication : ActionFilterAttribute 12 | { 13 | public override void OnActionExecuting(HttpActionContext actionContext) 14 | { 15 | base.OnActionExecuting(actionContext); 16 | 17 | bool authenticated = false; 18 | string message = "Access denied."; 19 | 20 | string apiKey = null; 21 | 22 | try 23 | { 24 | apiKey = actionContext.Request.GetApiKey(); 25 | 26 | // Make sure it's not null and it's 32 characters or we're wasting our time. 27 | if (apiKey != null && apiKey.Length == 32) 28 | { 29 | // Attempt to look up the api user. 30 | APIUser apiUser = APIUserManager.FindAndPrepare(apiKey); 31 | 32 | // Did we find one and is it ready to use? 33 | if (apiUser != null && apiUser.Prepared) 34 | { 35 | // Genuine API user. 36 | authenticated = true; 37 | } 38 | } 39 | } 40 | catch (Exception ex) 41 | { 42 | // Set appropriate message. 43 | message = "An error occurred while trying to authenticate this request."; 44 | 45 | EventLogManager.Log("AUTH_EXCEPTION", EventLogSeverity.Info, null, ex); 46 | } 47 | 48 | // If authentication failure occurs, return a response without carrying on executing actions. 49 | if (!authenticated) 50 | { 51 | EventLogManager.Log("AUTH_BAD_APIKEY", EventLogSeverity.Warning, string.Format("Authentication failed for API key: {0}.", apiKey)); 52 | 53 | actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, message); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PolyDeploy/Components/WebAPI/HttpRequestMessageExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Http; 4 | 5 | namespace Cantarus.Modules.PolyDeploy.Components.WebAPI 6 | { 7 | internal static class HttpRequestMessageExtensions 8 | { 9 | public static string GetApiKey(this HttpRequestMessage request) 10 | { 11 | if (request == null) 12 | { 13 | throw new ArgumentNullException(nameof(request)); 14 | } 15 | 16 | // Is there an api key header present? 17 | if (request.Headers.Contains("x-api-key")) 18 | { 19 | // Get the api key from the header. 20 | return request.Headers.GetValues("x-api-key").FirstOrDefault(); 21 | } 22 | 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PolyDeploy/Components/WebAPI/IPSpecController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using Cantarus.Modules.PolyDeploy.Components.Exceptions; 3 | using Cantarus.Modules.PolyDeploy.Components.WebAPI.ActionFilters; 4 | using DotNetNuke.Web.Api; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Web.Http; 11 | 12 | namespace Cantarus.Modules.PolyDeploy.Components.WebAPI 13 | { 14 | [RequireHost] 15 | [ValidateAntiForgeryToken] 16 | [InWhitelist] 17 | public class IPSpecController : DnnApiController 18 | { 19 | [HttpGet] 20 | public HttpResponseMessage GetAll() 21 | { 22 | List ipSpecs = IPSpecManager.GetAll().ToList(); 23 | 24 | return Request.CreateResponse(HttpStatusCode.OK, ipSpecs); 25 | } 26 | 27 | [HttpPost] 28 | public HttpResponseMessage Create(string name, string ip) 29 | { 30 | IPSpec ipSpec = null; 31 | 32 | try 33 | { 34 | ipSpec = IPSpecManager.Create(name, ip); 35 | } 36 | catch (IPSpecExistsException ex) 37 | { 38 | return Request.CreateErrorResponse(HttpStatusCode.Conflict, ex.Message); 39 | } 40 | catch (Exception ex) 41 | { 42 | return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex); 43 | } 44 | 45 | return Request.CreateResponse(HttpStatusCode.Created, ipSpec); 46 | } 47 | 48 | [HttpDelete] 49 | public HttpResponseMessage Delete(int id) 50 | { 51 | IPSpec ipSpec = IPSpecManager.GetById(id); 52 | 53 | if (ipSpec == null) 54 | { 55 | return Request.CreateErrorResponse(HttpStatusCode.NotFound, "IP spec not found."); 56 | } 57 | 58 | try 59 | { 60 | IPSpecManager.Delete(ipSpec); 61 | } 62 | catch (Exception ex) 63 | { 64 | return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Failed to delete IP spec."); 65 | } 66 | 67 | return Request.CreateResponse(HttpStatusCode.NoContent); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /PolyDeploy/Components/WebAPI/Routes.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Web.Api; 2 | 3 | namespace Cantarus.Modules.PolyDeploy.Components.WebAPI 4 | { 5 | public class Routes : IServiceRouteMapper 6 | { 7 | public void RegisterRoutes(IMapRoute mapRouteManager) 8 | { 9 | mapRouteManager.MapHttpRoute("PolyDeploy", "default", "{controller}/{action}", new[] { "Cantarus.Modules.PolyDeploy.Components.WebAPI" }); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PolyDeploy/Components/WebAPI/SettingController.cs: -------------------------------------------------------------------------------- 1 | using Cantarus.Modules.PolyDeploy.Components.DataAccess.Models; 2 | using Cantarus.Modules.PolyDeploy.Components.Exceptions; 3 | using Cantarus.Modules.PolyDeploy.Components.WebAPI.ActionFilters; 4 | using DotNetNuke.Web.Api; 5 | using System; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Web.Http; 9 | 10 | namespace Cantarus.Modules.PolyDeploy.Components.WebAPI 11 | { 12 | [RequireHost] 13 | [ValidateAntiForgeryToken] 14 | [InWhitelist] 15 | public class SettingController : DnnApiController 16 | { 17 | [HttpGet] 18 | public HttpResponseMessage Get(string group, string key) 19 | { 20 | Setting setting; 21 | 22 | try 23 | { 24 | setting = SettingManager.GetSetting(group, key); 25 | } 26 | catch (SettingNotFoundException ex) 27 | { 28 | return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex); 29 | } 30 | catch (Exception ex) 31 | { 32 | return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex); 33 | } 34 | 35 | return Request.CreateResponse(HttpStatusCode.OK, setting); 36 | } 37 | 38 | [HttpPost] 39 | public HttpResponseMessage Set(string group, string key, string value) 40 | { 41 | try 42 | { 43 | SettingManager.SetSetting(group, key, value); 44 | } 45 | catch (Exception ex) 46 | { 47 | return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex); 48 | } 49 | 50 | return Request.CreateResponse(HttpStatusCode.OK); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /PolyDeploy/Images/polydeploy-logo-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DNNCommunity/PolyDeploy/c6025af40c937b39d629af67a3cb986b3222e77f/PolyDeploy/Images/polydeploy-logo-icon.png -------------------------------------------------------------------------------- /PolyDeploy/License.txt: -------------------------------------------------------------------------------- 1 | 
2 |

License

3 |

4 | Cantarus http://www.cantarus.com
5 | Copyright (c) 2012
6 | by Cantarus
7 |

8 |
-------------------------------------------------------------------------------- /PolyDeploy/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | // 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | // 10 | [assembly: AssemblyTitle("Cantarus PolyDeploy")] 11 | [assembly: AssemblyDescription("A DotNetNuke Module from Cantarus Software")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("")] 15 | [assembly: AssemblyCopyright("2012 Cantarus")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(true)] 20 | // 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Revision and Build Numbers 29 | // by using the '*' as shown below: 30 | 31 | [assembly: AssemblyVersion("00.09.03.*")] 32 | [assembly: AssemblyDelaySign(false)] 33 | [assembly: AssemblyKeyFile("")] 34 | [assembly: AssemblyKeyName("")] 35 | 36 | -------------------------------------------------------------------------------- /PolyDeploy/ReleaseNotes.txt: -------------------------------------------------------------------------------- 1 | 

PolyDeploy

2 |

3 | Cantarus
4 | dotnetnuke@cantarus.com
5 | http://www.cantarus.com
6 |

7 |
8 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.01.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.1.0 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_APIUsers TABLE ******************************************************************/ 15 | /*********************************************************************************************************/ 16 | 17 | IF NOT EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUsers]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 18 | BEGIN 19 | CREATE TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUsers] 20 | ( 21 | [APIUserID] [INT] NOT NULL IDENTITY, 22 | [Name] [VARCHAR](64) NOT NULL UNIQUE, 23 | [APIKey] [VARCHAR](32) NOT NULL UNIQUE, 24 | [EncryptionKey] [VARCHAR](32) NOT NULL UNIQUE 25 | ) 26 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUsers] ADD CONSTRAINT [PK_{objectQualifier}Cantarus_PolyDeploy_APIUsers] PRIMARY KEY CLUSTERED ([APIUserID]) 27 | END 28 | GO 29 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.02.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.2.0 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_IPSpecs TABLE *******************************************************************/ 15 | /*********************************************************************************************************/ 16 | 17 | IF NOT EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_IPSpecs]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 18 | BEGIN 19 | CREATE TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_IPSpecs] 20 | ( 21 | [IPSpecID] [INT] NOT NULL IDENTITY, 22 | [Address] [VARCHAR](15) NOT NULL UNIQUE 23 | ) 24 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_IPSpecs] ADD CONSTRAINT [PK_{objectQualifier}Cantarus_PolyDeploy_IPSpecs] PRIMARY KEY CLUSTERED ([IPSpecID]) 25 | END 26 | GO 27 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.03.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.3.0 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_Sessions TABLE ******************************************************************/ 15 | /*********************************************************************************************************/ 16 | 17 | IF NOT EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 18 | BEGIN 19 | CREATE TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions] 20 | ( 21 | [SessionID] [INT] NOT NULL IDENTITY, 22 | [Guid] [VARCHAR](32) NOT NULL UNIQUE, 23 | [LastUsed] [DATETIME] NOT NULL 24 | ) 25 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions] ADD CONSTRAINT [PK_{objectQualifier}Cantarus_PolyDeploy_Sessions] PRIMARY KEY CLUSTERED ([SessionID]) 26 | END 27 | GO 28 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.04.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.4.0 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_Sessions TABLE ******************************************************************/ 15 | /*********************************************************************************************************/ 16 | 17 | IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 18 | IF NOT EXISTS(SELECT * FROM sys.columns WHERE name = 'Status' AND object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions]')) 19 | BEGIN 20 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions] ADD [Status] [INT] 21 | END 22 | GO 23 | 24 | IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 25 | IF NOT EXISTS(SELECT * FROM sys.columns WHERE name = 'Response' AND object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions]')) 26 | BEGIN 27 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Sessions] ADD [Response] [VARCHAR](MAX) 28 | END 29 | GO 30 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.06.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.6.0 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_EventLogs TABLE *****************************************************************/ 15 | /*********************************************************************************************************/ 16 | 17 | IF NOT EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_EventLogs]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 18 | BEGIN 19 | CREATE TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_EventLogs] 20 | ( 21 | [EventLogID] [INT] NOT NULL IDENTITY, 22 | [Date] [DATETIME] NOT NULL, 23 | [EventType] [VARCHAR](32) NOT NULL, 24 | [Severity] [INT] NOT NULL, 25 | [Message] [VARCHAR](MAX) NOT NULL, 26 | [StackTrace] [VARCHAR](MAX) 27 | ) 28 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_EventLogs] ADD CONSTRAINT [PK_{objectQualifier}Cantarus_PolyDeploy_EventLogs] PRIMARY KEY CLUSTERED ([EventLogID]) 29 | END 30 | GO 31 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.07.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.7.0 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_APIUsers TABLE ******************************************************************/ 15 | /*********************************************************************************************************/ 16 | 17 | IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUsers]') AND OBJECTPROPERTY(id, N'IsTable') = 1) 18 | IF NOT EXISTS(SELECT * FROM sys.columns WHERE name = 'BypassIPWhitelist' AND object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUsers]')) 19 | BEGIN 20 | ALTER TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_APIUsers] ADD [BypassIPWhitelist] [BIT] NOT NULL CONSTRAINT [{objectQualifier}DF_Cantarus_PolyDeploy_APIUsers_BypassIPWhitelist] DEFAULT (0) 21 | END 22 | GO 23 | -------------------------------------------------------------------------------- /PolyDeploy/SqlDataProviders/00.09.03.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | /***** SqlDataProvider *****/ 3 | /***** *****/ 4 | /***** *****/ 5 | /***** Note: To manually execute this script you must *****/ 6 | /***** perform a search and replace operation *****/ 7 | /***** for {databaseOwner} and {objectQualifier} *****/ 8 | /***** *****/ 9 | /************************************************************/ 10 | /*** Cantarus_PolyDeploy v0.9.3 ***/ 11 | /************************************************************/ 12 | 13 | /*********************************************************************************************************/ 14 | /*** Cantarus_PolyDeploy_Settings TABLE ******************************************************************/ 15 | /*** ***/ 16 | /*** Create new Settings table. ***/ 17 | /*********************************************************************************************************/ 18 | 19 | IF NOT EXISTS ( 20 | SELECT * 21 | FROM dbo.sysobjects 22 | WHERE id = object_id(N'{databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Settings]') 23 | AND OBJECTPROPERTY(id, N'IsTable') = 1) 24 | BEGIN 25 | CREATE TABLE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Settings] 26 | ( 27 | [SettingID] [INT] NOT NULL IDENTITY, 28 | [Group] [NVARCHAR](16) NOT NULL, 29 | [Key] [NVARCHAR](32) NOT NULL, 30 | [Value] [NVARCHAR](128) NOT NULL, 31 | 32 | CONSTRAINT [PK_{objectQualifier}Cantarus_PolyDeploy_Settings] 33 | PRIMARY KEY CLUSTERED ([SettingID]), 34 | 35 | CONSTRAINT [UQ_{objectQualifier}Cantarus_PolyDeploy_Settings_GroupKey] 36 | UNIQUE ([Group], [Key]) 37 | ) 38 | 39 | INSERT INTO {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Settings] ( 40 | [Group], 41 | [Key], 42 | [Value] 43 | ) 44 | VALUES ('WHITELIST', 'STATE', 'false') 45 | 46 | IF EXISTS ( 47 | SELECT * 48 | FROM {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_IPSpecs]) 49 | BEGIN 50 | UPDATE {databaseOwner}[{objectQualifier}Cantarus_PolyDeploy_Settings] 51 | SET [Value] = 'false' 52 | WHERE [Group] = 'WHITELIST' 53 | AND [Key] = 'STATE' 54 | END 55 | END 56 | GO 57 | -------------------------------------------------------------------------------- /PolyDeploy/module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DNNCommunity/PolyDeploy/c6025af40c937b39d629af67a3cb986b3222e77f/PolyDeploy/module.css -------------------------------------------------------------------------------- /PolyDeploy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "poly-deploy", 3 | "version": "0.9.3", 4 | "main": "app.js", 5 | "license": "Apache-2.0", 6 | "private": true, 7 | "dependencies": { 8 | "angular": "^1.4.3", 9 | "angular-file-upload": "^2.5.0", 10 | "angular-ui-router": "^0.2.15", 11 | "bootstrap": "^3.3.7" 12 | }, 13 | "devDependencies": { 14 | "babel-core": "^6.26.0", 15 | "babel-loader": "^7.1.2", 16 | "babel-plugin-transform-es2015-spread": "^6.22.0", 17 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 18 | "babel-polyfill": "^6.26.0", 19 | "babel-preset-es2015": "^6.24.1", 20 | "cheerio": "^1.0.0-rc.2", 21 | "css-loader": "^0.28.7", 22 | "extract-text-webpack-plugin": "^3.0.0", 23 | "file-loader": "^0.11.2", 24 | "gulp": "^4.0.2", 25 | "gulp-clean": "^0.4.0", 26 | "gulp-zip": "^5.0.2", 27 | "less": "^2.7.3", 28 | "less-loader": "^4.0.5", 29 | "merge-stream": "^1.0.1", 30 | "raw-loader": "^0.5.1", 31 | "style-loader": "^0.18.2", 32 | "webpack": "^3.8.1", 33 | "webpack-stream": "^4.0.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PolyDeploy/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PolyDeploy/project.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | /* 4 | Basics 5 | Standard configuration stuff, you shouldn't need to touch much other 6 | than MODULE_VERSION. 7 | */ 8 | MODULE_NAME: 'PolyDeploy', 9 | MODULE_DOMAIN: 'Modules', 10 | MODULE_VERSION: '00.09.03', 11 | WEBSITE_PATH: '../../Website/' 12 | }; 13 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/deploy-client.md: -------------------------------------------------------------------------------- 1 | # DeployClient 2 | The DeployClient provides a basic implementation of a client designed to interact with the WebAPI endpoints exposed by PolyDeploy to facilitate remote deployment of DNN modules. It demonstrates how the API key should be passed in the request and how after initiating an installation remotely the status of the install operation can be polled for updates. 3 | 4 | ## Setup 5 | To use the DeployClient the following three parameters are required: 6 | - `TargetUri`: the website URI of the DNN install you wish to target 7 | - `APIKey`: as the API Key of your API User 8 | - `EncryptionKey`: the Encryption Key of your API User 9 | 10 | There are two ways to configure your deploy client: Configuration File and Commandline Variables. 11 | 12 | ### Configuration File 13 | Add the above three parameters to the `DeployClient.exe.config`, found in the same directory as `DeployClient.exe`. 14 | 15 | Here is an example of a filled in `DeployClient.Properites.Settings` node. API and Encryption Keys have been redacted. 16 | 17 | ```xml 18 | ... 19 | 20 | 21 | http://mydevsite.dnndev.me/ 22 | 23 | 24 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 25 | 26 | 27 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 28 | 29 | 30 | ... 31 | ``` 32 | 33 | ### Commandline Variables 34 | When calling the Deploy Client, it is possible to pass in the above variables as arguments: 35 | ``` 36 | DeployClient.exe --target-uri http://mydevsite.dnndev.me/ --api-key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --encryption-key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 37 | ``` 38 | 39 | ## Usage 40 | Out of the box the DeployClient will identify any .zip files in the directory that it is being run from. 41 | 42 | 1. Place your module .zip files in the same directory as `DeployClient.exe`. 43 | 2. Open `DeployClient.exe` and follow the prompts. 44 | 45 | ## Additional Commandline Options 46 | The DeployClient features a small number of options that can tailor it to different situations, these are detailed below. 47 | 48 | - __--silent__ Don't print any output to stdout, don't prompt. 49 | - __--no-prompt__ Don't prompt. 50 | - __--help__ Display commandline help 51 | 52 | These options can be used on the command line like so `DeployClient.exe --silent`. 53 | -------------------------------------------------------------------------------- /docs/docgen-menu.md: -------------------------------------------------------------------------------- 1 | - PolyDeploy 2 | - [Installation](~/poly-deploy/installation.html) 3 | - [Quick Start Guide](~/poly-deploy/quick-start.html) 4 | - [API Users](~/poly-deploy/api-users.html) 5 | - [IP Whitelist](~/poly-deploy/ip-whitelist.html) 6 | - [API Reference](~/poly-deploy/api-reference.html) 7 | 8 | - [DeployClient](~/deploy-client.html) 9 | -------------------------------------------------------------------------------- /docs/img/polydeploy-logo-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DNNCommunity/PolyDeploy/c6025af40c937b39d629af67a3cb986b3222e77f/docs/img/polydeploy-logo-full.png -------------------------------------------------------------------------------- /docs/poly-deploy/api-reference.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | This document aims to serve as an API reference, detailing the API methods made available by PolyDeploy for the remote deployment of modules. 3 | 4 | ## IP Whitelist 5 | Before any of the following API methods will work, you must ensure that the IP address you are accessing the API from has been added to the IP Whitelist. [Read more][ip-whitelist]. 6 | 7 | ## API Authentication 8 | Before getting started with any of the following API methods, ensure you have created a new API User so that you can interact with the API. You'll need the API and Encryption Key pair that was generated when you created the API User. [Read more][api-users]. 9 | 10 | All requests sent to the api should contain your API Key in the `x-api-key` header of the request. 11 | 12 | ### CSharp 13 | ```csharp 14 | HttpClient client = new HttpClient(); 15 | 16 | client.DefaultRequestHeaders.Add("x-api-key", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 17 | ``` 18 | 19 | ### curl 20 | ```curl 21 | curl -H "x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 22 | ``` 23 | 24 | ## Methods 25 | 26 | ### GET CreateSession 27 | `/API/Remote/CreateSession` 28 | 29 | Allows the creation of a new deployment session. A valid deployment session is required for all subsequent actions you'll take when performing a remote deployment. 30 | 31 | _Returns:_ __string__ Guid representing the session. 32 | 33 | ### GET GetSession 34 | `/API/Remote/GetSession?sessionGuid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` 35 | 36 | Retrieves the `Session` object corresponding to the passed `sessionGuid`. 37 | 38 | _Returns:_ __Session__ Object representing the session in the database. 39 | 40 | ### POST AddPackages 41 | `/API/Remote/AddPackages?sessionGuid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` 42 | 43 | Adds packages to the session corresponding to the passed `sessionGuid`. 44 | 45 | ___Note:___ _Packages must be encrypted with the Encryption Key associated with the API Key being used to access the API._ 46 | 47 | ### GET Install 48 | `/API/Remote/Install?sessionGuid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` 49 | 50 | Starts the installation of the modules added to the session corresponding to the passed `sessionGuid`. 51 | 52 | [ip-whitelist]: ip-whitelist.html 53 | [api-users]: api-users.html 54 | -------------------------------------------------------------------------------- /docs/poly-deploy/api-users.md: -------------------------------------------------------------------------------- 1 | # API Users 2 | PolyDeploy makes use of API and Encryption Key pairs to authenticate requests to ensure that remote deployment of modules is only performed from appropraite clients. 3 | 4 | ___Note:___ _A new API User should be created for every client that uses PolyDeploy. This improves security and audability. It also means that in the event that an API and Encryption Key pair is compromised the minimum number of clients need to be updated with a replacement key pair._ 5 | 6 | ## Management 7 | You can manage API Users through the Management Utility. It is possible to create new API Users, view the existing API Users (although you are not able to see their existing API and Encryption Key pairs, this is intentional for security) and delete existing API Users. 8 | 9 | ___Note:___ _When you first create an API User a new API and Encryption Key pair will be generated and displayed to you. This will only happen once, subsequent visits to the Management Utility will not show the complete API and Encryption Key pairs._ 10 | -------------------------------------------------------------------------------- /docs/poly-deploy/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | The installation of the PolyDeploy module is mostly straight forward as you would expect from other DNN modules. There is however one additional step that must be taken to enable the module to function as intended, this is a small change to the `web.config` found in the DNN website root. 3 | 4 | ## Why? 5 | Some of the WebAPI endpoints provided by PolyDeploy make use of asynchronous methods. In its default configuration ASP.NET will dereference the `HttpContext` object after the `await` keyword is used in a method. Once the asynchronous operation completes and method execution continues the `HttpContext` object is no longer accessible. This is problematic as DNN relies on the `HttpContext` object for many of its internal functions. 6 | 7 | ## How? 8 | The issue is easily resolved with the addition of a configuration key to the `web.config`. The following code snippet shows the configuration key along with where it should be placed in the `web.config`. 9 | 10 | ```xml 11 | 12 | ... 13 | 14 | ... 15 | 16 | 17 | ... 18 | 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/poly-deploy/ip-whitelist.md: -------------------------------------------------------------------------------- 1 | # IP Whitelisting 2 | PolyDeploy makes use of an IP address whitelist as part of security measures to prevent modules from being deployed by unauthorised users. Currently only IPv4 addresses are supported. 3 | 4 | In addition to the addresses in the whitelist, localhost or 127.0.0.1 will always pass through. 5 | 6 | ___Note:___ _IP Whitelisting is still in development and should not be relied upon as a sole security measure._ 7 | 8 | ## Management 9 | The IP address whitelist can be managed through the Management Utility. Using the Management Utility you can add new IP addresses, view the existing whitelist and remove address from the whitelist. 10 | -------------------------------------------------------------------------------- /docs/poly-deploy/quick-start.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide 2 | The sections below should serve as quick start guides for using PolyDeploy to install modules both locally and remotely. 3 | 4 | ## Local Module Installations 5 | The simplest way to get started using PolyDeploy is to use the user interface to install modules locally. To get started simply follow the steps below: 6 | 7 | 1. Add the `UseTaskFriendlySynchronizationContext` configuration key to the `web.config` as described in [Installation][install-link]. 8 | 2. Install the PolyDeploy module using the DNN Extensions module. 9 | 3. Place the PolyDeploy module on a page. 10 | 4. You can now use the drag and drop user interface to install modules. 11 | 12 | ___Note:___ _Due to the IP Whitelisting functionality, you will only be able to access the user interface from the the host machine._ 13 | 14 | ## Remote Module Installations 15 | For more hands off module installations PolyDeploy can be used to install modules remotely. This also allows for PolyDeploy to be used for continuous integration. To get started simply follow the steps below: 16 | 17 | 1. Add the `UseTaskFriendlySynchronizationContext` configuration key to the `web.config` as described in [Installation][install-link]. 18 | 2. Install the PolyDeploy module using the DNN Extensions module. 19 | 3. Place the PolyDeploy module on a page. 20 | 4. Enter edit mode. 21 | 5. Click the edit icon on the PolyDeploy module and select 'Manage' from the edit menu. 22 | 6. In the management menu, select 'API Users'. 23 | 7. Create a new API User for your remote deployments. Take note of the provided API Key and Encryption Key as they will not be shown again. 24 | 8. In the management menu, select 'IP Whitelist'. 25 | 9. Create a new Whitelist Entry for the machine that you will be using to perform remote module installations. 26 | 10. You can now use your API Key and Encryption Key to perform remote module installations from the machine you added to the IP Whitelist. 27 | 28 | ___Note:___ _Due to the IP Whitelisting functionality, you will only be able to access the management user interface from machines that are already whitelisted._ 29 | 30 | [install-link]: installation.html 31 | --------------------------------------------------------------------------------