Compare commits

...

25 Commits

Author SHA1 Message Date
Michiel Hazelhof
b75dfb2512 Fix circleci 2025-08-12 15:42:52 +02:00
Michiel Hazelhof
232de042dd Fix type 2025-08-12 15:37:06 +02:00
Michiel Hazelhof
b12b470656 Cleanup circleci 2025-08-12 15:32:59 +02:00
Michiel Hazelhof
d07f030d9a Fix comparator 2025-08-12 15:32:49 +02:00
Michiel Hazelhof
1ee45a327f Fix path issue 2025-08-12 15:30:35 +02:00
Michiel Hazelhof
ab8eed492c Cleanup 2025-08-12 15:23:49 +02:00
Michiel Hazelhof
7203354204 Upgrade dnlib 2025-08-12 15:18:46 +02:00
Michiel Hazelhof
9ca720af8c Remove NewtonSoft.Json 2025-08-12 15:16:59 +02:00
Michiel Hazelhof
6ba14a7134 Cleanup 2025-08-12 15:15:31 +02:00
Michiel Hazelhof
d8098cb560 Cleanup 2025-08-12 15:03:54 +02:00
Michiel Hazelhof
c72fbf5b1c Reuse code 2025-08-12 15:03:48 +02:00
Michiel Hazelhof
0b0512570f Clarify language 2025-08-12 15:02:49 +02:00
Michiel Hazelhof
dfc364e7f3 Simplify call 2025-08-12 15:02:30 +02:00
Michiel Hazelhof
48c67fc66e Make call consistent 2025-08-12 15:02:21 +02:00
Michiel Hazelhof
80fcf0cfc6 Copy files only when needed 2025-08-12 15:01:57 +02:00
Michiel Hazelhof
e87bc81a9c Copy all files 2025-08-12 15:01:26 +02:00
Michiel Hazelhof
93cae61d66 Refactor and fixes 2025-08-12 13:42:08 +02:00
Michiel Hazelhof
52fabd9a95 Cleanup code 2025-08-12 13:11:12 +02:00
Michiel Hazelhof
ddf67ec706 Cleanup code 2025-08-12 12:35:54 +02:00
Michiel Hazelhof
7786c4406c Cleanup code 2025-08-12 12:18:28 +02:00
Michiel Hazelhof
f360f54e46 Update class path 2025-08-12 12:10:31 +02:00
Michiel Hazelhof
273ac7b4eb Fix ps1 script and update language 2025-08-12 12:09:48 +02:00
Michiel Hazelhof
d34041c1e3 Test generating user and organization licenses during build check (#252) 2025-08-05 12:05:05 +02:00
Michiel Hazelhof
de61195d19 Fix license generator according to upstream changes (#245) (#249) 2025-08-05 12:03:30 +02:00
Michiel Hazelhof
a97f6f3e49 Upstream patches 2025-07-15 10:52:18 +02:00
14 changed files with 306 additions and 274 deletions

View File

@@ -12,4 +12,10 @@ jobs:
command: ./generateKeys.sh command: ./generateKeys.sh
- run: - run:
name: Build script name: Build script
command: ./build.sh y 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

View File

@@ -1,3 +1,6 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
# define temporary directory # define temporary directory
$tempdirectory = "$pwd\temp" $tempdirectory = "$pwd\temp"
# define services to patch # define services to patch
@@ -25,12 +28,11 @@ if (!(Test-Path "$pwd\.keys")) {
.\generateKeys.ps1 .\generateKeys.ps1
} }
# copy the key to bitBetter and licenseGen # copy the key to bitBetter
Copy-Item "$pwd\.keys\cert.cert" -Destination "$pwd\src\bitBetter" Copy-Item "$pwd\.keys\cert.cert" -Destination "$pwd\src\bitBetter"
Copy-Item "$pwd\.keys\cert.pfx" -Destination "$pwd\src\licenseGen"
# build bitBetter and clean the source directory after # build bitBetter and clean the source directory after
docker build -t bitbetter/bitbetter "$pwd\src\bitBetter" docker build --no-cache -t bitbetter/bitbetter "$pwd\src\bitBetter"
Remove-Item "$pwd\src\bitBetter\cert.cert" -Force Remove-Item "$pwd\src\bitBetter\cert.cert" -Force
# gather all running instances # gather all running instances
@@ -43,13 +45,10 @@ foreach ($instance in $oldinstances) {
} }
# update bitwarden itself # update bitwarden itself
if ($args[0] -eq 'y') if ($args[0] -eq 'y') {
{
docker pull ghcr.io/bitwarden/self-host:beta docker pull ghcr.io/bitwarden/self-host:beta
} } else {
else $confirmation = Read-Host "Update (or get) bitwarden source container (y/n)"
{
$confirmation = Read-Host "Update (or get) bitwarden source container"
if ($confirmation -eq 'y') { if ($confirmation -eq 'y') {
docker pull ghcr.io/bitwarden/self-host:beta docker pull ghcr.io/bitwarden/self-host:beta
} }
@@ -82,23 +81,28 @@ docker build . --tag bitwarden-patch --file "$pwd\src\bitBetter\Dockerfile-bitwa
docker stop bitwarden-patch docker stop bitwarden-patch
docker rm bitwarden-patch docker rm bitwarden-patch
# copy our patched library to the licenseGen source directory
Copy-Item "$tempdirectory\Identity\Core.dll" -Destination "$pwd\src\licenseGen"
# remove our temporary directory
Remove-Item "$tempdirectory" -Recurse -Force
# start all user requested instances # start all user requested instances
foreach($line in Get-Content "$pwd\.servers\serverlist.txt") { if (Test-Path -Path "$pwd\.servers\serverlist.txt" -PathType Leaf) {
Invoke-Expression "& $line" foreach($line in Get-Content "$pwd\.servers\serverlist.txt") {
if (!($line.StartsWith("#"))) {
Invoke-Expression "& $line"
}
}
} }
# remove our bitBetter image # remove our bitBetter image
docker image rm bitbetter/bitbetter docker image rm bitbetter/bitbetter
# copy our patched library to the licenseGen source directory
Copy-Item "$tempdirectory\Identity\Core.dll" -Destination "$pwd\src\licenseGen"
Copy-Item "$pwd\.keys\cert.pfx" -Destination "$pwd\src\licenseGen"
# build the licenseGen # build the licenseGen
docker build -t bitbetter/licensegen "$pwd\src\licenseGen" docker build -t bitbetter/licensegen "$pwd\src\licenseGen"
# clean the licenseGen source directory # clean the licenseGen source directory
Remove-Item "$pwd\src\licenseGen\Core.dll" -Force Remove-Item "$pwd\src\licenseGen\Core.dll" -Force
Remove-Item "$pwd\src\licenseGen\cert.pfx" -Force Remove-Item "$pwd\src\licenseGen\cert.pfx" -Force
# remove our temporary directory
Remove-Item "$tempdirectory" -Recurse -Force

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# define temporary directory # define temporary directory
TEMPDIRECTORY="$PWD/temp" TEMPDIRECTORY="$PWD/temp"
@@ -28,12 +29,11 @@ if [ ! -d "$PWD/.keys" ]; then
./generateKeys.sh ./generateKeys.sh
fi fi
# copy the key to bitBetter and licenseGen # copy the key to bitBetter
cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter"
cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen"
# build bitBetter and clean the source directory after # build bitBetter and clean the source directory after
docker build -t bitbetter/bitbetter "$PWD/src/bitBetter" docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter"
rm -f "$PWD/src/bitBetter/cert.cert" rm -f "$PWD/src/bitBetter/cert.cert"
# gather all running instances # gather all running instances
@@ -46,10 +46,10 @@ for INSTANCE in ${OLDINSTANCES[@]}; do
done done
# update bitwarden itself # update bitwarden itself
if [ "$1" = "y" ]; then if [ "$1" = "update" ]; then
docker pull ghcr.io/bitwarden/self-host:beta docker pull ghcr.io/bitwarden/self-host:beta
else else
read -p "Update (or get) bitwarden source container: " -n 1 -r read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r
echo echo
if [[ $REPLY =~ ^[Yy]$ ]] if [[ $REPLY =~ ^[Yy]$ ]]
then then
@@ -84,24 +84,29 @@ docker build . --tag bitwarden-patch --file "$PWD/src/bitBetter/Dockerfile-bitwa
docker stop bitwarden-patch docker stop bitwarden-patch
docker rm bitwarden-patch docker rm bitwarden-patch
# copy our patched library to the licenseGen source directory
cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen"
# remove our temporary directory
rm -rf "$TEMPDIRECTORY"
# start all user requested instances # start all user requested instances
sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" if [ -f "$PWD/src/bitBetter/cert.cert" ]; then
cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do sed -i 's/\r$//' "$PWD/.servers/serverlist.txt"
bash -c "$LINE" cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do
done if [[ $LINE == "#*" ]] ;
bash -c "$LINE"
fi
done
fi
# remove our bitBetter image # remove our bitBetter image
docker image rm bitbetter/bitbetter docker image rm bitbetter/bitbetter
# copy our patched library to the licenseGen source directory
cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen"
cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen"
# build the licenseGen # build the licenseGen
docker build -t bitbetter/licensegen "$PWD/src/licenseGen" docker build -t bitbetter/licensegen "$PWD/src/licenseGen"
# clean the licenseGen source directory # clean the licenseGen source directory
rm -f "$PWD/src/licenseGen/Core.dll" rm -f "$PWD/src/licenseGen/Core.dll"
rm -f "$PWD/src/licenseGen/cert.pfx" rm -f "$PWD/src/licenseGen/cert.pfx"
# remove our temporary directory
rm -rf "$TEMPDIRECTORY"

View File

@@ -1,3 +1,6 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
# get the basic openssl binary path # get the basic openssl binary path
$opensslbinary = "$Env:Programfiles\OpenSSL-Win64\bin\openssl.exe" $opensslbinary = "$Env:Programfiles\OpenSSL-Win64\bin\openssl.exe"

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
# Check for openssl # Check for openssl
command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl required but not found. Aborting."; exit 1; } command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl required but not found. Aborting."; exit 1; }

View File

@@ -1,3 +1,6 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
if ($($args.Count) -lt 1) { if ($($args.Count) -lt 1) {
echo "USAGE: <License Gen action> [License Gen args...]" echo "USAGE: <License Gen action> [License Gen args...]"
echo "ACTIONS:" echo "ACTIONS:"
@@ -7,7 +10,7 @@ if ($($args.Count) -lt 1) {
Exit 1 Exit 1
} }
if ($args[0] = "interactive") { if ($args[0] -eq "interactive") {
docker run -it --rm bitbetter/licensegen interactive docker run -it --rm bitbetter/licensegen interactive
} else { } else {
docker run bitbetter/licensegen $args docker run bitbetter/licensegen $args

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
if [ $# -lt 1 ]; then if [ $# -lt 1 ]; then
echo "USAGE: <License Gen action> [License Gen args...]" echo "USAGE: <License Gen action> [License Gen args...]"

View File

@@ -11,4 +11,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0
WORKDIR /app WORKDIR /app
COPY --from=build /app . COPY --from=build /app .
ENTRYPOINT [ "/app/bitBetter" ] ENTRYPOINT ["dotnet", "/app/bitBetter.dll"]

View File

@@ -1,4 +1,3 @@
FROM ghcr.io/bitwarden/self-host:beta FROM ghcr.io/bitwarden/self-host:beta
COPY ./temp/Api/Core.dll /app/Api/Core.dll COPY ./temp/ /app/
COPY ./temp/Identity/Core.dll /app/Identity/Core.dll

View File

@@ -23,13 +23,8 @@ internal class Program
ModuleDefMD moduleDefMd = ModuleDefMD.Load(file); ModuleDefMD moduleDefMd = ModuleDefMD.Load(file);
Byte[] cert = File.ReadAllBytes(certFile); Byte[] cert = File.ReadAllBytes(certFile);
EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources.OfType<EmbeddedResource>().First(r => r.Name.Equals("Bit.Core.licensing.cer"));
.OfType<EmbeddedResource>() EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) { Attributes = embeddedResourceToRemove.Attributes };
.First(r => r.Name.Equals("Bit.Core.licensing.cer"));
Console.WriteLine(embeddedResourceToRemove.Name);
EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) {Attributes = embeddedResourceToRemove.Attributes };
moduleDefMd.Resources.Add(embeddedResourceToAdd); moduleDefMd.Resources.Add(embeddedResourceToAdd);
moduleDefMd.Resources.Remove(embeddedResourceToRemove); moduleDefMd.Resources.Remove(embeddedResourceToRemove);
@@ -41,14 +36,11 @@ internal class Program
Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}"); Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}");
IEnumerable<TypeDef> services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Services"); IEnumerable<TypeDef> services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Billing.Services");
TypeDef type = services.First(t => t.Name == "LicensingService"); TypeDef type = services.First(t => t.Name == "LicensingService");
MethodDef constructor = type.FindConstructors().First(); MethodDef constructor = type.FindConstructors().First();
Instruction instructionToPatch = Instruction instructionToPatch = constructor.Body.Instructions.FirstOrDefault(i => i.OpCode == OpCodes.Ldstr && String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase));
constructor.Body.Instructions
.FirstOrDefault(i => i.OpCode == OpCodes.Ldstr
&& String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase));
if (instructionToPatch != null) if (instructionToPatch != null)
{ {

View File

@@ -1,12 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="dnlib" Version="4.4.0" /> <PackageReference Include="dnlib" Version="4.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -12,4 +12,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0
WORKDIR /app WORKDIR /app
COPY --from=build /app . COPY --from=build /app .
ENTRYPOINT [ "dotnet", "/app/licenseGen.dll", "--core", "/app/Core.dll", "--cert", "/app/cert.pfx" ] ENTRYPOINT ["dotnet", "/app/licenseGen.dll", "--cert=/app/cert.pfx", "--core=/app/Core.dll"]

View File

@@ -1,57 +1,31 @@
using System; using System;
using System.IO; using System.IO;
using System.Text.Json;
using System.Reflection; using System.Reflection;
using System.Runtime.Loader; using System.Runtime.Loader;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using McMaster.Extensions.CommandLineUtils; using McMaster.Extensions.CommandLineUtils;
using Newtonsoft.Json;
namespace licenseGen; namespace licenseGen;
internal class Program internal class Program
{ {
private static readonly CommandLineApplication App = new();
private static readonly CommandOption Cert = App.Option("--cert", "Certifcate file", CommandOptionType.SingleValue);
private static readonly CommandOption CoreDll = App.Option("--core", "Path to Core.dll", CommandOptionType.SingleValue);
private static Int32 Main(String[] args) private static Int32 Main(String[] args)
{ {
CommandLineApplication app = new(); App.Command("interactive", config =>
CommandOption cert = app.Option("--cert", "cert file", CommandOptionType.SingleValue);
CommandOption coreDll = app.Option("--core", "path to core dll", CommandOptionType.SingleValue);
Boolean CertExists()
{
return File.Exists(cert.Value());
}
Boolean CoreExists()
{
return File.Exists(coreDll.Value());
}
Boolean VerifyTopOptions()
{
return !String.IsNullOrWhiteSpace(cert.Value()) &&
!String.IsNullOrWhiteSpace(coreDll.Value()) &&
CertExists() && CoreExists();
}
app.Command("interactive", config =>
{ {
String buff, licenseType = "", name = "", email = "", businessName=""; String buff, licenseType = "", name = "", email = "", businessName="";
Int16 storage = 0; Int16 storage = 0;
Boolean validGuid = false, validInstallid = false; Boolean validGuid = false, validInstallid = false;
Guid guid = new(), installid = new(); Guid guid = Guid.Empty, installid = Guid.Empty;
config.OnExecute(() => config.OnExecute(() =>
{ {
if (!VerifyTopOptions()) Check();
{
if (!CoreExists()) config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}");
if (!CertExists()) config.Error.WriteLine($"Can't find certificate at: {cert.Value()}");
config.ShowHelp();
return 1;
}
Console.WriteLine("Interactive license mode..."); Console.WriteLine("Interactive license mode...");
while (licenseType == "") while (licenseType == "")
@@ -59,51 +33,55 @@ internal class Program
Console.WriteLine("What would you like to generate, a [u]ser license or an [o]rg license: "); Console.WriteLine("What would you like to generate, a [u]ser license or an [o]rg license: ");
buff = Console.ReadLine(); buff = Console.ReadLine();
if(buff == "u") switch (buff)
{ {
licenseType = "user"; case "u":
Console.WriteLine("Okay, we will generate a user license.");
while (validGuid == false)
{ {
Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); licenseType = "user";
buff = Console.ReadLine(); Console.WriteLine("Okay, we will generate a user license.");
if (Guid.TryParse(buff, out guid))validGuid = true; while (!validGuid)
else Console.WriteLine("The user-guid provided does not appear to be valid!");
}
}
else if (buff == "o")
{
licenseType = "org";
Console.WriteLine("Okay, we will generate an organization license.");
while (validInstallid == false)
{
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"; Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: ");
} buff = Console.ReadLine();
else if (CheckBusinessName(buff))
{ if (Guid.TryParse(buff, out guid))validGuid = true;
businessName = buff; else Console.WriteLine("The user-guid provided does not appear to be valid!");
} }
break;
} }
} case "o":
else {
{ licenseType = "org";
Console.WriteLine("Unrecognized option \'" + buff + "\'."); 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;
} }
} }
@@ -111,7 +89,7 @@ internal class Program
{ {
Console.WriteLine("Please provide the username this license will be registered to. [username]: "); Console.WriteLine("Please provide the username this license will be registered to. [username]: ");
buff = Console.ReadLine(); buff = Console.ReadLine();
if ( CheckUsername(buff) ) name = buff; if (CheckUsername(buff)) name = buff;
} }
while (email == "") while (email == "")
@@ -141,39 +119,46 @@ internal class Program
} }
} }
if (licenseType == "user") switch (licenseType)
{ {
Console.WriteLine("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n"); case "user":
buff = Console.ReadLine();
if ( buff is "" or "y" or "Y" )
{ {
GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, guid, null); 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;
} }
else case "org":
{ {
Console.WriteLine("Exiting..."); Console.WriteLine("Confirm creation of \"organization\" license for business name: \"" + businessName + "\", username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", Install-ID: \"" + installid + "\"? Y/n");
return 0; buff = Console.ReadLine();
} if (buff is "" or "y" or "Y")
} {
else if (licenseType == "org") GenerateOrgLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, installid, businessName, null);
{ }
Console.WriteLine("Confirm creation of \"organization\" license for business name: \"" + businessName + "\", username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", Install-ID: \"" + installid + "\"? Y/n"); else
buff = Console.ReadLine(); {
if ( buff is "" or "y" or "Y" ) Console.WriteLine("Exiting...");
{ return 0;
GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, installid, businessName, null); }
}
else break;
{
Console.WriteLine("Exiting...");
return 0;
} }
} }
return 0; return 0;
}); });
}); });
app.Command("user", config => App.Command("user", config =>
{ {
CommandArgument name = config.Argument("Name", "your name"); CommandArgument name = config.Argument("Name", "your name");
CommandArgument email = config.Argument("Email", "your email"); CommandArgument email = config.Argument("Email", "your email");
@@ -183,20 +168,7 @@ internal class Program
config.OnExecute(() => config.OnExecute(() =>
{ {
if (!VerifyTopOptions()) Check();
{
if (!CoreExists())
{
config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}");
}
if (!CertExists())
{
config.Error.WriteLine($"Can't find certificate at: {cert.Value()}");
}
config.ShowHelp();
return 1;
}
if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value)) if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value))
{ {
@@ -225,12 +197,12 @@ internal class Program
storageShort = (Int16) parsedStorage; storageShort = (Int16) parsedStorage;
} }
GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value); GenerateUserLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value);
return 0; return 0;
}); });
}); });
app.Command("org", config => App.Command("org", config =>
{ {
CommandArgument name = config.Argument("Name", "your name"); CommandArgument name = config.Argument("Name", "your name");
CommandArgument email = config.Argument("Email", "your email"); CommandArgument email = config.Argument("Email", "your email");
@@ -241,24 +213,9 @@ internal class Program
config.OnExecute(() => config.OnExecute(() =>
{ {
if (!VerifyTopOptions()) Check();
{
if (!CoreExists())
{
config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}");
}
if (!CertExists())
{
config.Error.WriteLine($"Can't find certificate at: {cert.Value()}");
}
config.ShowHelp(); if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value) || String.IsNullOrWhiteSpace(installId.Value))
return 1;
}
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.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}' InstallId='{installId.Value}'");
config.ShowHelp(true); config.ShowHelp(true);
@@ -283,34 +240,54 @@ internal class Program
config.ShowHelp(true); config.ShowHelp(true);
return 1; return 1;
} }
storageShort = (Int16) parsedStorage; storageShort = (Int16)parsedStorage;
} }
GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value); GenerateOrgLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value);
return 0; return 0;
}); });
}); });
app.OnExecute(() => App.OnExecute(() =>
{ {
app.ShowHelp(); App.ShowHelp();
return 10; return 10;
}); });
app.HelpOption("-? | -h | --help");
try try
{ {
return app.Execute(args); App.HelpOption("-? | -h | --help");
return App.Execute(args);
} }
catch (Exception e) catch (Exception exception)
{ {
Console.Error.WriteLine("Oops: {0}", e); Console.Error.WriteLine("Oops: {0}", exception);
return 100; return 100;
} }
} }
private static void Check()
{
if (!File.Exists(Cert.Value()))
{
App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}");
App.ShowHelp();
Environment.Exit(1);
}
if (!File.Exists(CoreDll.Value()))
{
App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}");
App.ShowHelp();
Environment.Exit(1);
}
if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value()) || CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value()))
{
App.ShowHelp();
Environment.Exit(1);
}
}
// checkUsername Checks that the username is a valid username // checkUsername Checks that the username is a valid username
private static Boolean CheckUsername(String s) private static Boolean CheckUsername(String s)
{ {
@@ -356,100 +333,148 @@ internal class Program
return false; 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) private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key)
{ {
Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath));
Type type = core.GetType("Bit.Core.Models.Business.UserLicense"); Type type = core.GetType("Bit.Core.Billing.Models.Business.UserLicense");
Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); 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); Object license = Activator.CreateInstance(type);
Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); MethodInfo computeHash = type.GetMethod("ComputeHash");
Set("Id", userId); if (computeHash == null)
Set("Name", userName);
Set("Email", email);
Set("Premium", true);
Set("MaxStorageGb", storage == 0 ? Int16.MaxValue : storage);
Set("Version", 1);
Set("Issued", DateTime.UtcNow);
Set("Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1));
Set("Expires", DateTime.UtcNow.AddYears(100));
Set("Trial", false);
Set("LicenseType", Enum.Parse(licenseTypeEnum, "User"));
Set("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, [])));
Set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, [cert])));
Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented));
return;
void Set(String name, Object value)
{ {
type.GetProperty(name)?.SetValue(license, value); 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) 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(corePath); Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath));
Type type = core.GetType("Bit.Core.Billing.Organizations.Models.OrganizationLicense");
Type type = core.GetType("Bit.Core.Models.Business.OrganizationLicense");
Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType");
Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); 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); Object license = Activator.CreateInstance(type);
Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); MethodInfo computeHash = type.GetMethod("ComputeHash");
Set("InstallationId", instalId); if (computeHash == null)
Set("Id", Guid.NewGuid());
Set("Name", userName);
Set("BillingEmail", email);
Set("BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName);
Set("Enabled", true);
Set("Plan", "Enterprise (Annually)");
Set("PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually"));
Set("Seats", Int32.MaxValue);
Set("MaxCollections", Int16.MaxValue);
Set("UsePolicies", true);
Set("UseSso", true);
Set("UseKeyConnector", true);
Set("UseScim", true);
Set("UseGroups", true);
Set("UseEvents", true);
Set("UseDirectory", true);
Set("UseTotp", true);
Set("Use2fa", true);
Set("UseApi", true);
Set("UseResetPassword", true);
Set("UseCustomPermissions", true);
Set("MaxStorageGb", storage == 0 ? Int16.MaxValue : storage);
Set("SelfHost", true);
Set("UsersGetPremium", true);
Set("UsePasswordManager", true);
Set("UseSecretsManager", true);
Set("SmSeats", Int32.MaxValue);
Set("SmServiceAccounts", Int32.MaxValue);
Set("Version", 15); //This is set to 15 to use AllowAdminAccessToAllCollectionItems can be changed to 13 to just use Secrets Manager
Set("Issued", DateTime.UtcNow);
Set("Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1));
Set("Expires", DateTime.UtcNow.AddYears(100));
Set("Trial", false);
Set("LicenseType", Enum.Parse(licenseTypeEnum, "Organization"));
Set("LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now
Set("AllowAdminAccessToAllCollectionItems", true);
Set("UseRiskInsights", true);
Set("UseOrganizationDomains", true);
Set("UseAdminSponsoredFamilies", true);
Set("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, [])));
Set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, [cert])));
Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented));
return;
void Set(String name, Object value)
{ {
type.GetProperty(name)?.SetValue(license, value); 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, "UseCustomPermissions", true);
Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage);
Set(type, license, "SelfHost", true);
Set(type, license, "UsersGetPremium", true);
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, "Version", 15); //This is set to 15 to use AllowAdminAccessToAllCollectionItems can be changed to 13 to just use Secrets Manager
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, "Organization"));
Set(type, license, "LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now
Set(type, license, "AllowAdminAccessToAllCollectionItems", true);
Set(type, license, "UseRiskInsights", true);
Set(type, license, "UseOrganizationDomains", true);
Set(type, license, "UseAdminSponsoredFamilies", true);
Set(type, license, "UseRiskInsights", true);
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);
}
}

View File

@@ -1,14 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" /> <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" /> <PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>