diff --git a/.circleci/config.yml b/.circleci/config.yml index 443f66e..46a2f40 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,21 +1,21 @@ -version: 2.1 -jobs: - build: - machine: true - steps: - - checkout - - run: - name: Print the Current Time - command: date - - run: - name: Generate Keys - command: ./generateKeys.sh - - run: - name: Build script - command: ./build.sh update - - run: - name: Test generating user license - command: ./licenseGen.sh user TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 - - run: - name: Test generating organization license +version: 2.1 +jobs: + build: + machine: true + steps: + - checkout + - run: + name: Print the Current Time + command: date + - run: + name: Generate Keys + command: ./generateKeys.sh + - run: + name: Build script + command: ./build.sh update + - run: + name: Test generating user license + command: ./licenseGen.sh user TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 + - run: + name: Test generating organization license command: ./licenseGen.sh org TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d91b892 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root=true + +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style=tab +indent_size=4 +trim_trailing_whitespace=true +end_of_line=lf +charset=utf-8 + +[*.{cs}] +insert_final_newline=false + +[*.{md,mkdn}] +trim_trailing_whitespace = true +indent_style = space \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index b086bda..07764a7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.ps1 text eol=crlf +* text eol=lf \ No newline at end of file diff --git a/.servers/serverlist.txt b/.servers/serverlist.txt index c4267b0..fc35e33 100644 --- a/.servers/serverlist.txt +++ b/.servers/serverlist.txt @@ -1,4 +1,4 @@ -# Uncomment a line below and fill in the missing values or add your own. Every line in this file will be called by build.[sh|ps1] once the patched image is built. -# docker run -d --name bitwarden --restart=always -v \logs:/var/log/bitwarden -v \bwdata:/etc/bitwarden -p 80:8080 --env-file \settings.env bitwarden-patched -# -# docker-compose -f /docker-compose.yml up -d +# Uncomment a line below and fill in the missing values or add your own. Every line in this file will be called by build.[sh|ps1] once the patched image is built. +# docker run -d --name bitwarden --restart=always -v \logs:/var/log/bitwarden -v \bwdata:/etc/bitwarden -p 80:8080 --env-file \settings.env bitwarden-patched +# +# docker-compose -f /docker-compose.yml up -d diff --git a/README.md b/README.md index 31da987..803b2f0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ -# BitBetter +# BitBetter lite BitBetter is is a tool to modify Bitwarden's core dll to allow you to generate your own individual and organisation licenses. Please see the FAQ below for details on why this software was created. -_Beware! BitBetter does some semi janky stuff to rewrite the bitwarden core dll and allow the installation of a self signed certificate. Use at your own risk!_ +Be aware that this branch is **only** for the lite (formerly unified) version of bitwarden. It has been rewritten and works in different ways than the master branch. -Credit to https://github.com/h44z/BitBetter and https://github.com/jakeswenson/BitBetter +_Beware! BitBetter is a solution that generates a personal certificate and uses that to generate custom licences. This requires (automated) modifying of libraries. Use at your own risk!_ + +Credit to https://github.com/h44z/BitBetter and https://github.com/jakeswenson/BitBetter and https://github.com/GieltjE/BitBetter # Table of Contents - [BitBetter](#bitbetter) @@ -30,7 +32,7 @@ The following instructions are for unix-based systems (Linux, BSD, macOS) and Wi ## Dependencies Aside from docker, which you also need for Bitwarden, BitBetter requires the following: -* Bitwarden (tested with 1.47.1, might work on lower versions) +* Bitwarden (tested with 2025.11.1 might work on lower versions), for safety always stay up to date * openssl (probably already installed on most Linux or WSL systems, any version should work, on Windows it will be auto installed using winget) ## Setting up BitBetter @@ -156,6 +158,15 @@ docker exec bitwarden ln -s /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime Require a recreation of the docker container, build.sh will suffice too. +## Migrating from the old unified branch + +``` +git branch -m unified lite +git fetch origin +git branch -u origin/lite lite +git remote set-head origin -a +``` + # Footnotes 1This tool builds on top of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script. diff --git a/build.ps1 b/build.ps1 index 67f9618..8a494d3 100644 --- a/build.ps1 +++ b/build.ps1 @@ -4,6 +4,10 @@ $PSNativeCommandUseErrorActionPreference = $true # detect buildx, ErrorActionPreference will ensure the script stops execution if not found docker buildx version +# Enable BuildKit for better build experience and to ensure platform args are populated +$env:DOCKER_BUILDKIT=1 +$env:COMPOSE_DOCKER_CLI_BUILD=1 + # define temporary directory $tempdirectory = "$pwd\temp" # define services to patch diff --git a/build.sh b/build.sh index 64a5c6f..3adeba7 100755 --- a/build.sh +++ b/build.sh @@ -4,6 +4,10 @@ set -e # detect buildx, set -e will ensure the script stops execution if not found docker buildx version +# Enable BuildKit for better build experience and to ensure platform args are populated +export DOCKER_BUILDKIT=1 +export COMPOSE_DOCKER_CLI_BUILD=1 + # define temporary directory TEMPDIRECTORY="$PWD/temp" @@ -16,19 +20,19 @@ if [ -d "$TEMPDIRECTORY" ]; then fi if [ -f "$PWD/src/licenseGen/Core.dll" ]; then - rm -f "$PWD/src/licenseGen/Core.dll" + rm -f "$PWD/src/licenseGen/Core.dll" fi if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then - rm -f "$PWD/src/licenseGen/cert.pfx" + rm -f "$PWD/src/licenseGen/cert.pfx" fi if [ -f "$PWD/src/bitBetter/cert.cer" ]; then - rm -f "$PWD/src/bitBetter/cert.cer" + rm -f "$PWD/src/bitBetter/cert.cer" fi if [ -f "$PWD/.keys/cert.cert" ]; then - mv "$PWD/.keys/cert.cert" "$PWD/.keys/cert.cer" + mv "$PWD/.keys/cert.cert" "$PWD/.keys/cert.cer" fi # generate keys if none are available diff --git a/generateKeys.ps1 b/generateKeys.ps1 index 1a82714..fafbf95 100644 --- a/generateKeys.ps1 +++ b/generateKeys.ps1 @@ -1,25 +1,25 @@ -$ErrorActionPreference = 'Stop' -$PSNativeCommandUseErrorActionPreference = $true - -# get the basic openssl binary path -$opensslbinary = "$Env:Programfiles\OpenSSL-Win64\bin\openssl.exe" - -# if openssl is not installed attempt to install it -if (!(Get-Command $opensslbinary -errorAction SilentlyContinue)) -{ - winget install openssl -} - -# if previous keys exist, remove them -if (Test-Path "$pwd\.keys") -{ - Remove-Item "$pwd\.keys" -Recurse -Force -} - -# create new directory -New-item -ItemType Directory -Path "$pwd\.keys" - -# generate actual keys -Invoke-Expression "& '$opensslbinary' req -x509 -newkey rsa:4096 -keyout `"$pwd\.keys\key.pem`" -out `"$pwd\.keys\cert.cer`" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test" -Invoke-Expression "& '$opensslbinary' x509 -inform DER -in `"$pwd\.keys\cert.cer`" -out `"$pwd\.keys\cert.pem`"" +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + +# get the basic openssl binary path +$opensslbinary = "$Env:Programfiles\OpenSSL-Win64\bin\openssl.exe" + +# if openssl is not installed attempt to install it +if (!(Get-Command $opensslbinary -errorAction SilentlyContinue)) +{ + winget install openssl +} + +# if previous keys exist, remove them +if (Test-Path "$pwd\.keys") +{ + Remove-Item "$pwd\.keys" -Recurse -Force +} + +# create new directory +New-item -ItemType Directory -Path "$pwd\.keys" + +# generate actual keys +Invoke-Expression "& '$opensslbinary' req -x509 -newkey rsa:4096 -keyout `"$pwd\.keys\key.pem`" -out `"$pwd\.keys\cert.cer`" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test" +Invoke-Expression "& '$opensslbinary' x509 -inform DER -in `"$pwd\.keys\cert.cer`" -out `"$pwd\.keys\cert.pem`"" Invoke-Expression "& '$opensslbinary' pkcs12 -export -out `"$pwd\.keys\cert.pfx`" -inkey `"$pwd\.keys\key.pem`" -in `"$pwd\.keys\cert.pem`" -passin pass:test -passout pass:test" \ No newline at end of file diff --git a/licenseGen.ps1 b/licenseGen.ps1 index c9477b2..0bb802a 100644 --- a/licenseGen.ps1 +++ b/licenseGen.ps1 @@ -1,17 +1,17 @@ -$ErrorActionPreference = 'Stop' -$PSNativeCommandUseErrorActionPreference = $true - -if ($($args.Count) -lt 1) { - echo "USAGE: [License Gen args...]" - echo "ACTIONS:" - echo " interactive" - echo " user" - echo " org" - Exit 1 -} - -if ($args[0] -eq "interactive") { - docker run -it --rm bitbetter/licensegen interactive -} else { - docker run bitbetter/licensegen $args -} +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + +if ($($args.Count) -lt 1) { + echo "USAGE: [License Gen args...]" + echo "ACTIONS:" + echo " interactive" + echo " user" + echo " org" + Exit 1 +} + +if ($args[0] -eq "interactive") { + docker run -it --rm bitbetter/licensegen interactive +} else { + docker run bitbetter/licensegen $args +} diff --git a/licenseGen.sh b/licenseGen.sh index 3d93aa4..ad115e3 100755 --- a/licenseGen.sh +++ b/licenseGen.sh @@ -2,12 +2,12 @@ set -e if [ $# -lt 1 ]; then - echo "USAGE: [License Gen args...]" - echo "ACTIONS:" - echo " interactive" - echo " user" - echo " org" - exit 1 + echo "USAGE: [License Gen args...]" + echo "ACTIONS:" + echo " interactive" + echo " user" + echo " org" + exit 1 fi if [ "$1" = "interactive" ]; then diff --git a/src/bitBetter/Dockerfile-bitwarden-patch b/src/bitBetter/Dockerfile-bitwarden-patch index 4d6ec59..a3f7360 100644 --- a/src/bitBetter/Dockerfile-bitwarden-patch +++ b/src/bitBetter/Dockerfile-bitwarden-patch @@ -1,3 +1,3 @@ -FROM ghcr.io/bitwarden/lite:latest - -COPY ./temp/ /app/ +FROM ghcr.io/bitwarden/lite:latest + +COPY ./temp/ /app/ \ No newline at end of file diff --git a/src/bitBetter/Program.cs b/src/bitBetter/Program.cs index dec4970..902479e 100644 --- a/src/bitBetter/Program.cs +++ b/src/bitBetter/Program.cs @@ -1,67 +1,67 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using dnlib.DotNet; -using dnlib.DotNet.Emit; -using dnlib.DotNet.Writer; -using dnlib.IO; - -namespace bitBetter; - -internal class Program -{ - private static Int32 Main() - { - const String certFile = "/app/cert.cer"; - String[] files = Directory.GetFiles("/app/mount", "Core.dll", SearchOption.AllDirectories); - - foreach (String file in files) - { - Console.WriteLine(file); - ModuleDefMD moduleDefMd = ModuleDefMD.Load(file); - Byte[] cert = File.ReadAllBytes(certFile); - - EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources.OfType().First(r => r.Name.Equals("Bit.Core.licensing.cer")); - EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) { Attributes = embeddedResourceToRemove.Attributes }; - moduleDefMd.Resources.Add(embeddedResourceToAdd); - moduleDefMd.Resources.Remove(embeddedResourceToRemove); - - DataReader reader = embeddedResourceToRemove.CreateReader(); - X509Certificate2 existingCert = new(reader.ReadRemainingBytes()); - - Console.WriteLine($"Existing Cert Thumbprint: {existingCert.Thumbprint}"); - X509Certificate2 certificate = new(cert); - - Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}"); - - IEnumerable services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Billing.Services"); - TypeDef type = services.First(t => t.Name == "LicensingService"); - MethodDef constructor = type.FindConstructors().First(); - - Instruction instructionToPatch = constructor.Body.Instructions.FirstOrDefault(i => i.OpCode == OpCodes.Ldstr && String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase)); - - if (instructionToPatch != null) - { - instructionToPatch.Operand = certificate.Thumbprint; - } - else - { - Console.WriteLine("Can't find constructor to patch"); - } - - ModuleWriterOptions moduleWriterOptions = new(moduleDefMd); - moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.KeepOldMaxStack; - moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveAll; - moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveRids; - - moduleDefMd.Write(file + ".new"); - moduleDefMd.Dispose(); - File.Delete(file); - File.Move(file + ".new", file); - } - - return 0; - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; +using dnlib.IO; + +namespace bitBetter; + +internal class Program +{ + private static Int32 Main() + { + const String certFile = "/app/cert.cer"; + String[] files = Directory.GetFiles("/app/mount", "Core.dll", SearchOption.AllDirectories); + + foreach (String file in files) + { + Console.WriteLine(file); + ModuleDefMD moduleDefMd = ModuleDefMD.Load(file); + Byte[] cert = File.ReadAllBytes(certFile); + + EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources.OfType().First(r => r.Name.Equals("Bit.Core.licensing.cer")); + EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) { Attributes = embeddedResourceToRemove.Attributes }; + moduleDefMd.Resources.Add(embeddedResourceToAdd); + moduleDefMd.Resources.Remove(embeddedResourceToRemove); + + DataReader reader = embeddedResourceToRemove.CreateReader(); + X509Certificate2 existingCert = new(reader.ReadRemainingBytes()); + + Console.WriteLine($"Existing Cert Thumbprint: {existingCert.Thumbprint}"); + X509Certificate2 certificate = new(cert); + + Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}"); + + IEnumerable services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Billing.Services"); + TypeDef type = services.First(t => t.Name == "LicensingService"); + MethodDef constructor = type.FindConstructors().First(); + + Instruction instructionToPatch = constructor.Body.Instructions.FirstOrDefault(i => i.OpCode == OpCodes.Ldstr && String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase)); + + if (instructionToPatch != null) + { + instructionToPatch.Operand = certificate.Thumbprint; + } + else + { + Console.WriteLine("Can't find constructor to patch"); + } + + ModuleWriterOptions moduleWriterOptions = new(moduleDefMd); + moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.KeepOldMaxStack; + moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveAll; + moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveRids; + + moduleDefMd.Write(file + ".new"); + moduleDefMd.Dispose(); + File.Delete(file); + File.Move(file + ".new", file); + } + + return 0; + } } \ No newline at end of file diff --git a/src/bitBetter/bitBetter.csproj b/src/bitBetter/bitBetter.csproj index 6433b92..8ddb699 100644 --- a/src/bitBetter/bitBetter.csproj +++ b/src/bitBetter/bitBetter.csproj @@ -3,7 +3,7 @@ Exe net8.0 - - + + - + \ No newline at end of file diff --git a/src/licenseGen/Dockerfile b/src/licenseGen/Dockerfile index 1c8eb9b..601bf87 100644 --- a/src/licenseGen/Dockerfile +++ b/src/licenseGen/Dockerfile @@ -12,4 +12,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 WORKDIR /app COPY --from=build /app . -ENTRYPOINT ["dotnet", "/app/licenseGen.dll", "--cert=/app/cert.pfx", "--core=/app/Core.dll"] +ENTRYPOINT ["dotnet", "/app/licenseGen.dll", "--cert=/app/cert.pfx", "--core=/app/Core.dll"] \ No newline at end of file diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index a417af7..39e10ec 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -1,485 +1,485 @@ -using System; -using System.IO; -using System.Text.Json; -using System.Reflection; -using System.Runtime.Loader; -using System.Security.Cryptography.X509Certificates; -using McMaster.Extensions.CommandLineUtils; - -namespace licenseGen; - -internal class Program -{ - private static readonly CommandLineApplication App = new(); - private static readonly CommandOption Cert = App.Option("--cert", "Certificate file", CommandOptionType.SingleValue); - private static readonly CommandOption CoreDll = App.Option("--core", "Path to Core.dll", CommandOptionType.SingleValue); - - private static Int32 Main(String[] args) - { - App.Command("interactive", config => - { - String buff, licenseType = "", name = "", email = "", businessName=""; - Int16 storage = 0; - Boolean validGuid = false, validInstallid = false; - Guid guid = Guid.Empty, installid = Guid.Empty; - - config.OnExecute(() => - { - Check(); - Console.WriteLine("Interactive license mode..."); - - while (licenseType == "") - { - Console.WriteLine("What would you like to generate, a [u]ser license or an [o]rg license: "); - buff = Console.ReadLine(); - - switch (buff) - { - case "u": - { - licenseType = "user"; - Console.WriteLine("Okay, we will generate a user license."); - - while (!validGuid) - { - Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); - buff = Console.ReadLine(); - - if (Guid.TryParse(buff, out guid))validGuid = true; - else Console.WriteLine("The user-guid provided does not appear to be valid!"); - } - break; - } - case "o": - { - licenseType = "org"; - Console.WriteLine("Okay, we will generate an organization license."); - - while (!validInstallid) - { - Console.WriteLine("Please provide your Bitwarden Install-ID — refer to the Readme for details on how to retrieve this. [Install-ID]: "); - buff = Console.ReadLine(); - - if (Guid.TryParse(buff, out installid)) validInstallid = true; - else Console.WriteLine("The install-id provided does not appear to be valid."); - } - - while (businessName == "") - { - Console.WriteLine("Please enter a business name, default is BitBetter. [Business Name]: "); - buff = Console.ReadLine(); - if (buff == "") - { - businessName = "BitBetter"; - } - else if (CheckBusinessName(buff)) - { - businessName = buff; - } - } - break; - } - default: - Console.WriteLine("Unrecognized option \'" + buff + "\'."); - break; - } - } - - while (name == "") - { - Console.WriteLine("Please provide the username this license will be registered to. [username]: "); - buff = Console.ReadLine(); - if (CheckUsername(buff)) name = buff; - } - - while (email == "") - { - Console.WriteLine("Please provide the email address for the user " + name + ". [email]: "); - buff = Console.ReadLine(); - if (CheckEmail(buff)) - { - email = buff; - } - } - - while (storage == 0) - { - Console.WriteLine("Extra storage space for the user " + name + ". (max.: " + Int16.MaxValue + "). Defaults to maximum value. [storage]"); - buff = Console.ReadLine(); - if (String.IsNullOrWhiteSpace(buff)) - { - storage = Int16.MaxValue; - } - else - { - if (CheckStorage(buff)) - { - storage = Int16.Parse(buff); - } - } - } - - switch (licenseType) - { - case "user": - { - Console.WriteLine("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n"); - buff = Console.ReadLine(); - if (buff is "" or "y" or "Y") - { - GenerateUserLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, guid, null); - } - else - { - Console.WriteLine("Exiting..."); - return 0; - } - - break; - } - case "org": - { - Console.WriteLine("Confirm creation of \"organization\" license for business name: \"" + businessName + "\", username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", Install-ID: \"" + installid + "\"? Y/n"); - buff = Console.ReadLine(); - if (buff is "" or "y" or "Y") - { - GenerateOrgLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, installid, businessName, null); - } - else - { - Console.WriteLine("Exiting..."); - return 0; - } - - break; - } - } - - return 0; - }); - }); - App.Command("user", config => - { - CommandArgument name = config.Argument("Name", "your name"); - CommandArgument email = config.Argument("Email", "your email"); - CommandArgument userIdArg = config.Argument("User ID", "your user id"); - CommandArgument storage = config.Argument("Storage", "extra storage space in GB. Maximum is " + Int16.MaxValue + " (optional, default = max)"); - CommandArgument key = config.Argument("Key", "your key id (optional)"); - - config.OnExecute(() => - { - Check(); - - if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value)) - { - config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'"); - config.ShowHelp(true); - return 1; - } - - if (String.IsNullOrWhiteSpace(userIdArg.Value) || !Guid.TryParse(userIdArg.Value, out Guid userId)) - { - config.Error.WriteLine("User ID not provided"); - config.ShowHelp(true); - return 1; - } - - Int16 storageShort = 0; - if (!String.IsNullOrWhiteSpace(storage.Value)) - { - Double parsedStorage = Double.Parse(storage.Value); - if (parsedStorage is > Int16.MaxValue or < 0) - { - config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]"); - config.ShowHelp(true); - return 1; - } - storageShort = (Int16) parsedStorage; - } - - GenerateUserLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value); - - return 0; - }); - }); - App.Command("org", config => - { - CommandArgument name = config.Argument("Name", "your name"); - CommandArgument email = config.Argument("Email", "your email"); - CommandArgument installId = config.Argument("InstallId", "your installation id (GUID)"); - CommandArgument storage = config.Argument("Storage", "extra storage space in GB. Maximum is " + Int16.MaxValue + " (optional, default = max)"); - CommandArgument businessName = config.Argument("BusinessName", "name for the organization (optional)"); - CommandArgument key = config.Argument("Key", "your key id (optional)"); - - config.OnExecute(() => - { - Check(); - - if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value) || String.IsNullOrWhiteSpace(installId.Value)) - { - config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}' InstallId='{installId.Value}'"); - config.ShowHelp(true); - return 1; - } - - if (!Guid.TryParse(installId.Value, out Guid installationId)) - { - config.Error.WriteLine("Unable to parse your installation id as a GUID"); - config.Error.WriteLine($"Here's a new guid: {Guid.NewGuid()}"); - config.ShowHelp(true); - return 1; - } - - Int16 storageShort = 0; - if (!String.IsNullOrWhiteSpace(storage.Value)) - { - Double parsedStorage = Double.Parse(storage.Value); - if (parsedStorage is > Int16.MaxValue or < 0) - { - config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]"); - config.ShowHelp(true); - return 1; - } - storageShort = (Int16)parsedStorage; - } - - GenerateOrgLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value); - - return 0; - }); - }); - - App.OnExecute(() => - { - App.ShowHelp(); - return 10; - }); - - try - { - App.HelpOption("-? | -h | --help"); - return App.Execute(args); - } - catch (Exception exception) - { - Console.Error.WriteLine("Oops: {0}", exception); - return 100; - } - } - - private static void Check() - { - if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value())) - { - App.Error.WriteLine("No certificate specified"); - App.ShowHelp(); - Environment.Exit(1); - } - else if (CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) - { - App.Error.WriteLine("No core dll specified"); - App.ShowHelp(); - Environment.Exit(1); - } - else if (!File.Exists(Cert.Value())) - { - App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}"); - App.ShowHelp(); - Environment.Exit(1); - } - else if (!File.Exists(CoreDll.Value())) - { - App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}"); - App.ShowHelp(); - Environment.Exit(1); - } - } - - // checkUsername Checks that the username is a valid username - private static Boolean CheckUsername(String s) - { - // TODO: Actually validate - if (!String.IsNullOrWhiteSpace(s)) return true; - - Console.WriteLine("The username provided doesn't appear to be valid!"); - return false; - } - - // checkBusinessName Checks that the Business Name is a valid username - private static Boolean CheckBusinessName(String s) - { - // TODO: Actually validate - if (!String.IsNullOrWhiteSpace(s)) return true; - - Console.WriteLine("The Business Name provided doesn't appear to be valid!"); - return false; - } - - // checkEmail Checks that the email address is a valid email address - private static Boolean CheckEmail(String s) - { - // TODO: Actually validate - if (!String.IsNullOrWhiteSpace(s)) return true; - - Console.WriteLine("The email provided doesn't appear to be valid!"); - return false; - } - - // checkStorage Checks that the storage is in a valid range - private static Boolean CheckStorage(String s) - { - if (String.IsNullOrWhiteSpace(s)) - { - Console.WriteLine("The storage provided doesn't appear to be valid!"); - return false; - } - - if (!(Double.Parse(s) > Int16.MaxValue) && !(Double.Parse(s) < 0)) return true; - - Console.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]!"); - return false; - } - - private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; - private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key) - { - Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); - - Type type = core.GetType("Bit.Core.Billing.Models.Business.UserLicense"); - Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); - - if (type == null) - { - Console.WriteLine("Could not find type!"); - return; - } - if (licenseTypeEnum == null) - { - Console.WriteLine("Could not find license licenseTypeEnum!"); - return; - } - - Object license = Activator.CreateInstance(type); - - MethodInfo computeHash = type.GetMethod("ComputeHash"); - if (computeHash == null) - { - Console.WriteLine("Could not find ComputeHash!"); - return; - } - - MethodInfo sign = type.GetMethod("Sign"); - if (sign == null) - { - Console.WriteLine("Could not find sign!"); - return; - } - - Set(type, license, "LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); - Set(type, license, "Id", userId); - Set(type, license, "Name", userName); - Set(type, license, "Email", email); - Set(type, license, "Premium", true); - Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); - Set(type, license, "Version", 1); - Set(type, license, "Issued", DateTime.UtcNow); - Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); - Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); - Set(type, license, "Trial", false); - Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "User")); - Set(type, license, "Hash", Convert.ToBase64String(((Byte[])computeHash.Invoke(license, []))!)); - Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); - - Console.WriteLine(JsonSerializer.Serialize(license, JsonOptions)); - } - private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) - { - Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); - Type type = core.GetType("Bit.Core.Billing.Organizations.Models.OrganizationLicense"); - Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); - Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); - - if (type == null) - { - Console.WriteLine("Could not find type!"); - return; - } - if (licenseTypeEnum == null) - { - Console.WriteLine("Could not find licenseTypeEnum!"); - return; - } - if (planTypeEnum == null) - { - Console.WriteLine("Could not find planTypeEnum!"); - return; - } - - Object license = Activator.CreateInstance(type); - - MethodInfo computeHash = type.GetMethod("ComputeHash"); - if (computeHash == null) - { - Console.WriteLine("Could not find ComputeHash!"); - return; - } - - MethodInfo sign = type.GetMethod("Sign"); - if (sign == null) - { - Console.WriteLine("Could not find sign!"); - return; - } - - Set(type, license, "LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); - Set(type, license, "InstallationId", instalId); - Set(type, license, "Id", Guid.NewGuid()); - Set(type, license, "Name", userName); - Set(type, license, "BillingEmail", email); - Set(type, license, "BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName); - Set(type, license, "Enabled", true); - Set(type, license, "Plan", "Enterprise (Annually)"); - Set(type, license, "PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually")); - Set(type, license, "Seats", Int32.MaxValue); - Set(type, license, "MaxCollections", Int16.MaxValue); - Set(type, license, "UsePolicies", true); - Set(type, license, "UseSso", true); - Set(type, license, "UseKeyConnector", true); - Set(type, license, "UseScim", true); - Set(type, license, "UseGroups", true); - Set(type, license, "UseEvents", true); - Set(type, license, "UseDirectory", true); - Set(type, license, "UseTotp", true); - Set(type, license, "Use2fa", true); - Set(type, license, "UseApi", true); - Set(type, license, "UseResetPassword", true); - Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); - Set(type, license, "SelfHost", true); - Set(type, license, "UsersGetPremium", true); - Set(type, license, "UseCustomPermissions", true); - Set(type, license, "Version", 16); - Set(type, license, "Issued", DateTime.UtcNow); - Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); - Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); - Set(type, license, "ExpirationWithoutGracePeriod", DateTime.UtcNow.AddYears(100)); - Set(type, license, "UsePasswordManager", true); - Set(type, license, "UseSecretsManager", true); - Set(type, license, "SmSeats", Int32.MaxValue); - Set(type, license, "SmServiceAccounts", Int32.MaxValue); - Set(type, license, "UseRiskInsights", true); - Set(type, license, "LimitCollectionCreationDeletion", true); - Set(type, license, "AllowAdminAccessToAllCollectionItems", true); - Set(type, license, "Trial", false); - Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); - Set(type, license, "UseOrganizationDomains", true); - Set(type, license, "UseAdminSponsoredFamilies", true); - Set(type, license, "Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); - Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); - - Console.WriteLine(JsonSerializer.Serialize(license, JsonOptions)); - } - private static void Set(Type type, Object license, String name, Object value) - { - type.GetProperty(name)?.SetValue(license, value); - } +using System; +using System.IO; +using System.Text.Json; +using System.Reflection; +using System.Runtime.Loader; +using System.Security.Cryptography.X509Certificates; +using McMaster.Extensions.CommandLineUtils; + +namespace licenseGen; + +internal class Program +{ + private static readonly CommandLineApplication App = new(); + private static readonly CommandOption Cert = App.Option("--cert", "Certificate file", CommandOptionType.SingleValue); + private static readonly CommandOption CoreDll = App.Option("--core", "Path to Core.dll", CommandOptionType.SingleValue); + + private static Int32 Main(String[] args) + { + App.Command("interactive", config => + { + String buff, licenseType = "", name = "", email = "", businessName=""; + Int16 storage = 0; + Boolean validGuid = false, validInstallid = false; + Guid guid = Guid.Empty, installid = Guid.Empty; + + config.OnExecute(() => + { + Check(); + Console.WriteLine("Interactive license mode..."); + + while (licenseType == "") + { + Console.WriteLine("What would you like to generate, a [u]ser license or an [o]rg license: "); + buff = Console.ReadLine(); + + switch (buff) + { + case "u": + { + licenseType = "user"; + Console.WriteLine("Okay, we will generate a user license."); + + while (!validGuid) + { + Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); + buff = Console.ReadLine(); + + if (Guid.TryParse(buff, out guid))validGuid = true; + else Console.WriteLine("The user-guid provided does not appear to be valid!"); + } + break; + } + case "o": + { + licenseType = "org"; + Console.WriteLine("Okay, we will generate an organization license."); + + while (!validInstallid) + { + Console.WriteLine("Please provide your Bitwarden Install-ID — refer to the Readme for details on how to retrieve this. [Install-ID]: "); + buff = Console.ReadLine(); + + if (Guid.TryParse(buff, out installid)) validInstallid = true; + else Console.WriteLine("The install-id provided does not appear to be valid."); + } + + while (businessName == "") + { + Console.WriteLine("Please enter a business name, default is BitBetter. [Business Name]: "); + buff = Console.ReadLine(); + if (buff == "") + { + businessName = "BitBetter"; + } + else if (CheckBusinessName(buff)) + { + businessName = buff; + } + } + break; + } + default: + Console.WriteLine("Unrecognized option \'" + buff + "\'."); + break; + } + } + + while (name == "") + { + Console.WriteLine("Please provide the username this license will be registered to. [username]: "); + buff = Console.ReadLine(); + if (CheckUsername(buff)) name = buff; + } + + while (email == "") + { + Console.WriteLine("Please provide the email address for the user " + name + ". [email]: "); + buff = Console.ReadLine(); + if (CheckEmail(buff)) + { + email = buff; + } + } + + while (storage == 0) + { + Console.WriteLine("Extra storage space for the user " + name + ". (max.: " + Int16.MaxValue + "). Defaults to maximum value. [storage]"); + buff = Console.ReadLine(); + if (String.IsNullOrWhiteSpace(buff)) + { + storage = Int16.MaxValue; + } + else + { + if (CheckStorage(buff)) + { + storage = Int16.Parse(buff); + } + } + } + + switch (licenseType) + { + case "user": + { + Console.WriteLine("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n"); + buff = Console.ReadLine(); + if (buff is "" or "y" or "Y") + { + GenerateUserLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, guid, null); + } + else + { + Console.WriteLine("Exiting..."); + return 0; + } + + break; + } + case "org": + { + Console.WriteLine("Confirm creation of \"organization\" license for business name: \"" + businessName + "\", username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", Install-ID: \"" + installid + "\"? Y/n"); + buff = Console.ReadLine(); + if (buff is "" or "y" or "Y") + { + GenerateOrgLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, installid, businessName, null); + } + else + { + Console.WriteLine("Exiting..."); + return 0; + } + + break; + } + } + + return 0; + }); + }); + App.Command("user", config => + { + CommandArgument name = config.Argument("Name", "your name"); + CommandArgument email = config.Argument("Email", "your email"); + CommandArgument userIdArg = config.Argument("User ID", "your user id"); + CommandArgument storage = config.Argument("Storage", "extra storage space in GB. Maximum is " + Int16.MaxValue + " (optional, default = max)"); + CommandArgument key = config.Argument("Key", "your key id (optional)"); + + config.OnExecute(() => + { + Check(); + + if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value)) + { + config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'"); + config.ShowHelp(true); + return 1; + } + + if (String.IsNullOrWhiteSpace(userIdArg.Value) || !Guid.TryParse(userIdArg.Value, out Guid userId)) + { + config.Error.WriteLine("User ID not provided"); + config.ShowHelp(true); + return 1; + } + + Int16 storageShort = 0; + if (!String.IsNullOrWhiteSpace(storage.Value)) + { + Double parsedStorage = Double.Parse(storage.Value); + if (parsedStorage is > Int16.MaxValue or < 0) + { + config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]"); + config.ShowHelp(true); + return 1; + } + storageShort = (Int16) parsedStorage; + } + + GenerateUserLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value); + + return 0; + }); + }); + App.Command("org", config => + { + CommandArgument name = config.Argument("Name", "your name"); + CommandArgument email = config.Argument("Email", "your email"); + CommandArgument installId = config.Argument("InstallId", "your installation id (GUID)"); + CommandArgument storage = config.Argument("Storage", "extra storage space in GB. Maximum is " + Int16.MaxValue + " (optional, default = max)"); + CommandArgument businessName = config.Argument("BusinessName", "name for the organization (optional)"); + CommandArgument key = config.Argument("Key", "your key id (optional)"); + + config.OnExecute(() => + { + Check(); + + if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value) || String.IsNullOrWhiteSpace(installId.Value)) + { + config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}' InstallId='{installId.Value}'"); + config.ShowHelp(true); + return 1; + } + + if (!Guid.TryParse(installId.Value, out Guid installationId)) + { + config.Error.WriteLine("Unable to parse your installation id as a GUID"); + config.Error.WriteLine($"Here's a new guid: {Guid.NewGuid()}"); + config.ShowHelp(true); + return 1; + } + + Int16 storageShort = 0; + if (!String.IsNullOrWhiteSpace(storage.Value)) + { + Double parsedStorage = Double.Parse(storage.Value); + if (parsedStorage is > Int16.MaxValue or < 0) + { + config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]"); + config.ShowHelp(true); + return 1; + } + storageShort = (Int16)parsedStorage; + } + + GenerateOrgLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value); + + return 0; + }); + }); + + App.OnExecute(() => + { + App.ShowHelp(); + return 10; + }); + + try + { + App.HelpOption("-? | -h | --help"); + return App.Execute(args); + } + catch (Exception exception) + { + Console.Error.WriteLine("Oops: {0}", exception); + return 100; + } + } + + private static void Check() + { + if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value())) + { + App.Error.WriteLine("No certificate specified"); + App.ShowHelp(); + Environment.Exit(1); + } + else if (CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) + { + App.Error.WriteLine("No core dll specified"); + App.ShowHelp(); + Environment.Exit(1); + } + else if (!File.Exists(Cert.Value())) + { + App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}"); + App.ShowHelp(); + Environment.Exit(1); + } + else if (!File.Exists(CoreDll.Value())) + { + App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}"); + App.ShowHelp(); + Environment.Exit(1); + } + } + + // checkUsername Checks that the username is a valid username + private static Boolean CheckUsername(String s) + { + // TODO: Actually validate + if (!String.IsNullOrWhiteSpace(s)) return true; + + Console.WriteLine("The username provided doesn't appear to be valid!"); + return false; + } + + // checkBusinessName Checks that the Business Name is a valid username + private static Boolean CheckBusinessName(String s) + { + // TODO: Actually validate + if (!String.IsNullOrWhiteSpace(s)) return true; + + Console.WriteLine("The Business Name provided doesn't appear to be valid!"); + return false; + } + + // checkEmail Checks that the email address is a valid email address + private static Boolean CheckEmail(String s) + { + // TODO: Actually validate + if (!String.IsNullOrWhiteSpace(s)) return true; + + Console.WriteLine("The email provided doesn't appear to be valid!"); + return false; + } + + // checkStorage Checks that the storage is in a valid range + private static Boolean CheckStorage(String s) + { + if (String.IsNullOrWhiteSpace(s)) + { + Console.WriteLine("The storage provided doesn't appear to be valid!"); + return false; + } + + if (!(Double.Parse(s) > Int16.MaxValue) && !(Double.Parse(s) < 0)) return true; + + Console.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]!"); + return false; + } + + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key) + { + Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); + + Type type = core.GetType("Bit.Core.Billing.Models.Business.UserLicense"); + Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); + + if (type == null) + { + Console.WriteLine("Could not find type!"); + return; + } + if (licenseTypeEnum == null) + { + Console.WriteLine("Could not find license licenseTypeEnum!"); + return; + } + + Object license = Activator.CreateInstance(type); + + MethodInfo computeHash = type.GetMethod("ComputeHash"); + if (computeHash == null) + { + Console.WriteLine("Could not find ComputeHash!"); + return; + } + + MethodInfo sign = type.GetMethod("Sign"); + if (sign == null) + { + Console.WriteLine("Could not find sign!"); + return; + } + + Set(type, license, "LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); + Set(type, license, "Id", userId); + Set(type, license, "Name", userName); + Set(type, license, "Email", email); + Set(type, license, "Premium", true); + Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); + Set(type, license, "Version", 1); + Set(type, license, "Issued", DateTime.UtcNow); + Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); + Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); + Set(type, license, "Trial", false); + Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "User")); + Set(type, license, "Hash", Convert.ToBase64String(((Byte[])computeHash.Invoke(license, []))!)); + Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); + + Console.WriteLine(JsonSerializer.Serialize(license, JsonOptions)); + } + private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) + { + Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); + Type type = core.GetType("Bit.Core.Billing.Organizations.Models.OrganizationLicense"); + Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); + Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); + + if (type == null) + { + Console.WriteLine("Could not find type!"); + return; + } + if (licenseTypeEnum == null) + { + Console.WriteLine("Could not find licenseTypeEnum!"); + return; + } + if (planTypeEnum == null) + { + Console.WriteLine("Could not find planTypeEnum!"); + return; + } + + Object license = Activator.CreateInstance(type); + + MethodInfo computeHash = type.GetMethod("ComputeHash"); + if (computeHash == null) + { + Console.WriteLine("Could not find ComputeHash!"); + return; + } + + MethodInfo sign = type.GetMethod("Sign"); + if (sign == null) + { + Console.WriteLine("Could not find sign!"); + return; + } + + Set(type, license, "LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); + Set(type, license, "InstallationId", instalId); + Set(type, license, "Id", Guid.NewGuid()); + Set(type, license, "Name", userName); + Set(type, license, "BillingEmail", email); + Set(type, license, "BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName); + Set(type, license, "Enabled", true); + Set(type, license, "Plan", "Enterprise (Annually)"); + Set(type, license, "PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually")); + Set(type, license, "Seats", Int32.MaxValue); + Set(type, license, "MaxCollections", Int16.MaxValue); + Set(type, license, "UsePolicies", true); + Set(type, license, "UseSso", true); + Set(type, license, "UseKeyConnector", true); + Set(type, license, "UseScim", true); + Set(type, license, "UseGroups", true); + Set(type, license, "UseEvents", true); + Set(type, license, "UseDirectory", true); + Set(type, license, "UseTotp", true); + Set(type, license, "Use2fa", true); + Set(type, license, "UseApi", true); + Set(type, license, "UseResetPassword", true); + Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); + Set(type, license, "SelfHost", true); + Set(type, license, "UsersGetPremium", true); + Set(type, license, "UseCustomPermissions", true); + Set(type, license, "Version", 16); + Set(type, license, "Issued", DateTime.UtcNow); + Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); + Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); + Set(type, license, "ExpirationWithoutGracePeriod", DateTime.UtcNow.AddYears(100)); + Set(type, license, "UsePasswordManager", true); + Set(type, license, "UseSecretsManager", true); + Set(type, license, "SmSeats", Int32.MaxValue); + Set(type, license, "SmServiceAccounts", Int32.MaxValue); + Set(type, license, "UseRiskInsights", true); + Set(type, license, "LimitCollectionCreationDeletion", true); + Set(type, license, "AllowAdminAccessToAllCollectionItems", true); + Set(type, license, "Trial", false); + Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); + Set(type, license, "UseOrganizationDomains", true); + Set(type, license, "UseAdminSponsoredFamilies", true); + Set(type, license, "Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); + Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); + + Console.WriteLine(JsonSerializer.Serialize(license, JsonOptions)); + } + private static void Set(Type type, Object license, String name, Object value) + { + type.GetProperty(name)?.SetValue(license, value); + } } \ No newline at end of file diff --git a/src/licenseGen/licenseGen.csproj b/src/licenseGen/licenseGen.csproj index 6e03496..ba128a9 100644 --- a/src/licenseGen/licenseGen.csproj +++ b/src/licenseGen/licenseGen.csproj @@ -7,4 +7,4 @@ - + \ No newline at end of file