mirror of
https://github.com/jakeswenson/BitBetter.git
synced 2023-10-10 13:36:57 +02:00
* Initial work * Fix typo * Fix typo * Fix stupid issue * Add comments and fix minor issues * Add extra information * Add Linux script for generating keys * Add circleci * Add comments * Add extra option * Add missing permissions and empty script for now * Fix line endings * Add missing mount point * Simplify patch * Fix scripts * Reduce complexity * Fix circleci * Remove useless line * Move to src folder and improve image creation
This commit is contained in:
parent
b819fe0c7d
commit
d4abc9e5b7
@ -1,15 +1,15 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Print the Current Time
|
||||
command: date
|
||||
- run:
|
||||
name: Generate Keys
|
||||
command: ./.keys/generate-keys.sh
|
||||
- run:
|
||||
name: Build script
|
||||
command: ./build.sh
|
||||
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 y
|
3
.servers/serverlist.txt
Normal file
3
.servers/serverlist.txt
Normal file
@ -0,0 +1,3 @@
|
||||
docker run -d --name bitwarden -v <full-local-path>\logs:/var/log/bitwarden -v <full-local-path>\bwdata:/etc/bitwarden -p 80:8080 --env-file <full-local-path>\settings.env bitwarden-patch
|
||||
<OR>
|
||||
docker-compose -f <full-local-path>/docker-compose.yml up -d
|
134
README.md
134
README.md
@ -1,10 +1,10 @@
|
||||
# BitBetter
|
||||
|
||||
BitBetter is is a tool to modify Bitwarden's core dll to allow you to generate your own individual and organisation licenses. **You must have an existing installation of Bitwarden for BitBetter to modify.**
|
||||
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 janky stuff to rewrite the bitwarden core dll and allow the installation of a self signed certificate. Use at your own risk!_
|
||||
_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!_
|
||||
|
||||
Credit to https://github.com/h44z/BitBetter and https://github.com/jakeswenson/BitBetter
|
||||
|
||||
@ -25,55 +25,70 @@ Credit to https://github.com/h44z/BitBetter and https://github.com/jakeswenson/B
|
||||
- [Footnotes](#footnotes)
|
||||
|
||||
# Getting Started
|
||||
The following instructions are for unix-based systems (Linux, BSD, macOS), it is possible to use a Windows systems assuming you are able to enable and install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
|
||||
The following instructions are for unix-based systems (Linux, BSD, macOS) and Windows, just choose the correct script extension (.sh or .ps1 respectively).
|
||||
|
||||
## 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)
|
||||
* openssl (probably already installed on most Linux or WSL systems, any version should work)
|
||||
* 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
|
||||
With your dependencies installed, begin the installation of BitBetter by downloading it through Github or using the git command:
|
||||
|
||||
```bash
|
||||
```
|
||||
git clone https://github.com/jakeswenson/BitBetter.git
|
||||
```
|
||||
|
||||
## Building BitBetter
|
||||
|
||||
Now that you've set up your build environment, you can **run the main build script** to generate a modified version of the `bitwarden/api` and `bitwarden/identity` docker images.
|
||||
Now that you've set up your build environment, we need to specify which servers to start after the work is done.
|
||||
The scripts supports running and patching multi instances.
|
||||
|
||||
Edit the .servers/serverlist.txt file and fill in the missing values, they can be replaced with existing installation values.
|
||||
This file may be empty, but there will be no containers will be spun up automatically.
|
||||
|
||||
Now it is time to **run the main build script** to generate a modified version of the `bitwarden/self-host` docker image and the license generator.
|
||||
|
||||
From the BitBetter directory, simply run:
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
./build.[sh|ps1]
|
||||
```
|
||||
|
||||
This will create a new self-signed certificate in the `.keys` directory if one does not already exist and then create a modified version of the official `bitwarden/api` called `bitbetter/api` and a modified version of the `bitwarden/identity` called `bitbetter/identity`.
|
||||
This will create a new self-signed certificate in the `.keys` directory if one does not already exist and then create a modified version of the official `bitwarden/self-host` image called `bitwarden-patch`.
|
||||
|
||||
You may now simply create the file `/path/to/bwdata/docker/docker-compose.override.yml` with the following contents to utilize the modified images.
|
||||
Afterwards it will automatically generate the license generator and start all previously specified containers which are **now ready to accept self-issued licenses.**
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
api:
|
||||
image: bitbetter/api
|
||||
|
||||
identity:
|
||||
image: bitbetter/identity
|
||||
```
|
||||
|
||||
You'll also want to edit the `/path/to/bwdata/scripts/run.sh` file. In the `function restart()` block, comment out the call to `dockerComposePull`.
|
||||
|
||||
> Replace `dockerComposePull`<br>with `#dockerComposePull`
|
||||
|
||||
You can now start or restart Bitwarden as normal and the modified api will be used. **It is now ready to accept self-issued licenses.**
|
||||
|
||||
---
|
||||
|
||||
## Updating Bitwarden and BitBetter
|
||||
|
||||
To update Bitwarden, the same `build.[sh|ps1]` script can be used. It will rebuild the BitBetter image and automatically update Bitwarden before doing so.
|
||||
|
||||
## Generating Signed Licenses
|
||||
|
||||
There is a tool included in the directory `licenseGen/` that will generate new individual and organization licenses. These licenses will be accepted by the modified Bitwarden because they will be signed by the certificate you generated in earlier steps.
|
||||
|
||||
In order to run the tool and generate a license you'll need to get a **user's GUID** in order to generate an **invididual license** or the server's **install ID** to generate an **Organization license**. These can be retrieved most easily through the Bitwarden [Admin Portal](https://help.bitwarden.com/article/admin-portal/).
|
||||
|
||||
**The user must have a verified email address at the time of license import, otherwise Bitwarden will reject the license key. Nevertheless, the license key can be generated even before the user's email is verified.**
|
||||
|
||||
If you ran the build script, you can **simply run the license gen in interactive mode** from the `Bitbetter` directory and **follow the prompts to generate your license**.
|
||||
|
||||
```
|
||||
./licenseGen.[sh|ps1] interactive
|
||||
```
|
||||
|
||||
**The license generator will spit out a JSON-formatted license which can then be used within the Bitwarden web front-end to license your user or org!**
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Note: Manually generating Certificate & Key
|
||||
|
||||
If you wish to generate your self-signed cert & key manually, you can run the following commands.
|
||||
Note that you should never have to do this yourself, but can also be triggered by running the generateKeys.[sh|ps1] script.
|
||||
|
||||
```bash
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.cert -days 36500 -outform DER -passout pass:test
|
||||
@ -81,69 +96,11 @@ openssl x509 -inform DER -in cert.cert -out cert.pem
|
||||
openssl pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem -passin pass:test -passout pass:test
|
||||
```
|
||||
|
||||
> Note that the password here must be `test`.<sup>[1](#f1)</sup>
|
||||
> Note that the password here must be `test`.<sup>[2](#f1)</sup>
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Updating Bitwarden and BitBetter
|
||||
|
||||
To update Bitwarden, the provided `update-bitwarden.sh` script can be used. It will rebuild the BitBetter images and automatically update Bitwarden afterwards. Docker pull errors can be ignored for api and identity images.
|
||||
|
||||
You can either run this script without providing any parameters in interactive mode (`./update-bitwarden.sh`) or by setting the parameters as follows, to run the script in non-interactive mode:
|
||||
```bash
|
||||
./update-bitwarden.sh param1 param2 param3
|
||||
```
|
||||
`param1`: The path to the directory containing your bwdata directory
|
||||
|
||||
`param2`: If you want the docker-compose file to be overwritten (either `y` or `n`)
|
||||
|
||||
`param3`: If you want the bitbetter images to be rebuild (either `y` or `n`)
|
||||
|
||||
If you are updating from versions <= 1.46.2, you may need to run `update-bitwarden.sh` twice to complete the update process.
|
||||
|
||||
## Generating Signed Licenses
|
||||
|
||||
There is a tool included in the directory `src/licenseGen/` that will generate new individual and organization licenses. These licenses will be accepted by the modified Bitwarden because they will be signed by the certificate you generated in earlier steps.
|
||||
|
||||
|
||||
First, from the `BitBetter/src/licenseGen` directory, **build the license generator**.<sup>[2](#f2)</sup>
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
In order to run the tool and generate a license you'll need to get a **user's GUID** in order to generate an **invididual license** or the server's **install ID** to generate an **Organization license**. These can be retrieved most easily through the Bitwarden [Admin Portal](https://help.bitwarden.com/article/admin-portal/).
|
||||
|
||||
**The user must have a verified email address at the time of license import, otherwise Bitwarden will reject the license key. Nevertheless, the license key can be generated even before the user's email is verified.**
|
||||
|
||||
If you generated your keys in the default `BitBetter/.keys` directory, you can **simply run the license gen in interactive mode** from the `Bitbetter` directory and **follow the prompts to generate your license**.
|
||||
|
||||
```bash
|
||||
./src/licenseGen/run.sh interactive
|
||||
```
|
||||
|
||||
**The license generator will spit out a JSON-formatted license which can then be used within the Bitwarden web front-end to license your user or org!**
|
||||
|
||||
---
|
||||
|
||||
### Note: Alternative Ways to Generate License
|
||||
|
||||
If you wish to run the license gen from a directory aside from the root `BitBetter` one, you'll have to provide the absolute path to your cert.pfx.
|
||||
|
||||
```bash
|
||||
./src/licenseGen/run.sh /Absolute/Path/To/BitBetter/.keys/cert.pfx interactive
|
||||
```
|
||||
|
||||
Additional, instead of interactive mode, you can also pass the parameters directly to the command as follows.
|
||||
|
||||
```bash
|
||||
./src/licenseGen/run.sh /Absolute/Path/To/BitBetter/.keys/cert.pfx user "Name" "E-Mail" "User-GUID" ["Storage Space in GB"] ["Custom LicenseKey"]
|
||||
./src/licenseGen/run.sh /Absolute/Path/To/BitBetter/.keys/cert.pfx org "Name" "E-Mail" "Install-ID used to install the server" ["Storage Space in GB"] ["Custom LicenseKey"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
# FAQ: Questions you might have.
|
||||
|
||||
## Why build a license generator for open source software?
|
||||
@ -161,7 +118,6 @@ UPDATE: Bitwarden now offers a cheap license called [Families Organization](http
|
||||
|
||||
# Footnotes
|
||||
|
||||
<a name="#f1"><sup>1</sup></a> If you wish to change this you'll need to change the value that `src/licenseGen/Program.cs` uses for its `GenerateUserLicense` and `GenerateOrgLicense` calls. Remember, this is really unnecessary as this certificate does not represent any type of security-related certificate.
|
||||
|
||||
<a name="#f2"><sup>2</sup></a>This tool builds on top of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script.
|
||||
<a name="#f1"><sup>1</sup></a>This tool builds on top of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script.
|
||||
|
||||
<a name="#f2"><sup>2</sup></a> If you wish to change this you'll need to change the value that `licenseGen/Program.cs` uses for its `GenerateUserLicense` and `GenerateOrgLicense` calls. Remember, this is really unnecessary as this certificate does not represent any type of security-related certificate.
|
||||
|
104
build.ps1
Normal file
104
build.ps1
Normal file
@ -0,0 +1,104 @@
|
||||
# define temporary directory
|
||||
$tempdirectory = "$pwd\temp"
|
||||
# define services to patch
|
||||
$components = "Api","Identity"
|
||||
|
||||
# delete old directories / files if applicable
|
||||
if (Test-Path "$tempdirectory") {
|
||||
Remove-Item "$tempdirectory" -Recurse -Force
|
||||
}
|
||||
|
||||
if (Test-Path -Path "$pwd\src\licenseGen\Core.dll" -PathType Leaf) {
|
||||
Remove-Item "$pwd\src\licenseGen\Core.dll" -Force
|
||||
}
|
||||
|
||||
if (Test-Path -Path "$pwd\src\licenseGen\cert.pfx" -PathType Leaf) {
|
||||
Remove-Item "$pwd\src\licenseGen\cert.pfx" -Force
|
||||
}
|
||||
|
||||
if (Test-Path -Path "$pwd\src\bitBetter\cert.cert" -PathType Leaf) {
|
||||
Remove-Item "$pwd\src\bitBetter\cert.cert" -Force
|
||||
}
|
||||
|
||||
# generate keys if none are available
|
||||
if (!(Test-Path "$pwd\.keys")) {
|
||||
.\generateKeys.ps1
|
||||
}
|
||||
|
||||
# copy the key to bitBetter and licenseGen
|
||||
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
|
||||
docker build -t bitbetter/bitbetter "$pwd\src\bitBetter"
|
||||
Remove-Item "$pwd\src\bitBetter\cert.cert" -Force
|
||||
|
||||
# gather all running instances
|
||||
$oldinstances = docker container ps --all -f Name=bitwarden --format '{{.ID}}'
|
||||
|
||||
# stop all running instances
|
||||
foreach ($instance in $oldinstances) {
|
||||
docker stop $instance
|
||||
docker rm $instance
|
||||
}
|
||||
|
||||
# update bitwarden itself
|
||||
if ($args[0] -eq 'y')
|
||||
{
|
||||
docker pull bitwarden/self-host:beta
|
||||
}
|
||||
else
|
||||
{
|
||||
$confirmation = Read-Host "Update (or get) bitwarden source container"
|
||||
if ($confirmation -eq 'y') {
|
||||
docker pull bitwarden/self-host:beta
|
||||
}
|
||||
}
|
||||
|
||||
# stop and remove previous existing patch(ed) container
|
||||
docker stop bitwarden-patch
|
||||
docker rm bitwarden-patch
|
||||
docker image rm bitwarden-patch
|
||||
|
||||
# start a new bitwarden instance so we can patch it
|
||||
$patchinstance = docker run -d --name bitwarden-patch bitwarden/self-host:beta
|
||||
|
||||
# create our temporary directory
|
||||
New-item -ItemType Directory -Path $tempdirectory
|
||||
|
||||
# extract the files that need to be patched from the services that need to be patched into our temporary directory
|
||||
foreach ($component in $components) {
|
||||
New-item -itemtype Directory -path "$tempdirectory\$component"
|
||||
docker cp $patchinstance`:/app/$component/Core.dll "$tempdirectory\$component\Core.dll"
|
||||
}
|
||||
|
||||
# run bitBetter, this applies our patches to the required files
|
||||
docker run -v "$tempdirectory`:/app/mount" --rm bitbetter/bitbetter
|
||||
|
||||
# create a new image with the patched files
|
||||
docker build . --tag bitwarden-patch --file "$pwd\src\bitBetter\Dockerfile-bitwarden-patch"
|
||||
|
||||
# stop and remove our temporary container
|
||||
docker stop 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
|
||||
foreach($line in Get-Content "$pwd\.servers\serverlist.txt") {
|
||||
Invoke-Expression "& $line"
|
||||
}
|
||||
|
||||
# remove our bitBetter image
|
||||
docker image rm bitbetter/bitbetter
|
||||
|
||||
# build the licenseGen
|
||||
docker build -t bitbetter/licensegen "$pwd\src\licenseGen"
|
||||
|
||||
# clean the licenseGen source directory
|
||||
Remove-Item "$pwd\src\licenseGen\Core.dll" -Force
|
||||
Remove-Item "$pwd\src\licenseGen\cert.pfx" -Force
|
116
build.sh
116
build.sh
@ -1,28 +1,106 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
DIR=`dirname "$0"`
|
||||
DIR=`exec 2>/dev/null;(cd -- "$DIR") && cd -- "$DIR"|| cd "$DIR"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
|
||||
BW_VERSION=$(curl -sL https://go.btwrdn.co/bw-sh-versions | grep '^ *"'coreVersion'":' | awk -F\: '{ print $2 }' | sed -e 's/,$//' -e 's/^"//' -e 's/"$//')
|
||||
# define temporary directory
|
||||
TEMPDIRECTORY="$PWD/temp"
|
||||
|
||||
echo "Building BitBetter for BitWarden version $BW_VERSION"
|
||||
# define services to patch
|
||||
COMPONENTS=("Api" "Identity")
|
||||
|
||||
# If there aren't any keys, generate them first.
|
||||
[ -e "$DIR/.keys/cert.cert" ] || "$DIR/.keys/generate-keys.sh"
|
||||
# delete old directories / files if applicable
|
||||
if [ -d "$TEMPDIRECTORY" ]; then
|
||||
rm -rf "$TEMPDIRECTORY"
|
||||
fi
|
||||
|
||||
[ -e "$DIR/src/bitBetter/.keys" ] || mkdir "$DIR/src/bitBetter/.keys"
|
||||
if [ -f "$PWD/src/licenseGen/Core.dll" ]; then
|
||||
rm -f "$PWD/src/licenseGen/Core.dll"
|
||||
fi
|
||||
|
||||
cp "$DIR/.keys/cert.cert" "$DIR/src/bitBetter/.keys"
|
||||
if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then
|
||||
rm -f "$PWD/src/licenseGen/cert.pfx"
|
||||
fi
|
||||
|
||||
docker run --rm -v "$DIR/src/bitBetter:/bitBetter" -w=/bitBetter mcr.microsoft.com/dotnet/sdk:6.0 sh build.sh
|
||||
if [ -f "$PWD/src/bitBetter/cert.cert" ]; then
|
||||
rm -f "$PWD/src/bitBetter/cert.cert"
|
||||
fi
|
||||
|
||||
docker build --no-cache --build-arg BITWARDEN_TAG=bitwarden/api:$BW_VERSION --label com.bitwarden.product="bitbetter" -t bitbetter/api "$DIR/src/bitBetter" # --squash
|
||||
docker build --no-cache --build-arg BITWARDEN_TAG=bitwarden/identity:$BW_VERSION --label com.bitwarden.product="bitbetter" -t bitbetter/identity "$DIR/src/bitBetter" # --squash
|
||||
# generate keys if none are available
|
||||
if [ ! -d "$PWD/.keys" ]; then
|
||||
./generateKeys.sh
|
||||
fi
|
||||
|
||||
docker tag bitbetter/api bitbetter/api:latest
|
||||
docker tag bitbetter/identity bitbetter/identity:latest
|
||||
docker tag bitbetter/api bitbetter/api:$BW_VERSION
|
||||
docker tag bitbetter/identity bitbetter/identity:$BW_VERSION
|
||||
# copy the key to bitBetter and licenseGen
|
||||
cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter"
|
||||
cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen"
|
||||
|
||||
# Remove old instances of the image after a successful build.
|
||||
ids=$( docker images bitbetter/* | grep -E -v -- "CREATED|latest|${BW_VERSION}" | awk '{ print $3 }' )
|
||||
[ -n "$ids" ] && docker rmi $ids || true
|
||||
# build bitBetter and clean the source directory after
|
||||
docker build -t bitbetter/bitbetter "$PWD/src/bitBetter"
|
||||
rm -f "$PWD/src/bitBetter/cert.cert"
|
||||
|
||||
# gather all running instances
|
||||
OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}')
|
||||
|
||||
# stop all running instances
|
||||
for INSTANCE in ${OLDINSTANCES[@]}; do
|
||||
docker stop $INSTANCE
|
||||
docker rm $INSTANCE
|
||||
done
|
||||
|
||||
# update bitwarden itself
|
||||
if [ "$1" = "y" ]; then
|
||||
docker pull bitwarden/self-host:beta
|
||||
else
|
||||
read -p "Update (or get) bitwarden source container: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
docker pull bitwarden/self-host:beta
|
||||
fi
|
||||
fi
|
||||
|
||||
# stop and remove previous existing patch(ed) container
|
||||
docker stop bitwarden-patch
|
||||
docker rm bitwarden-patch
|
||||
docker image rm bitwarden-patch
|
||||
|
||||
# start a new bitwarden instance so we can patch it
|
||||
PATCHINSTANCE=$(docker run -d --name bitwarden-patch bitwarden/self-host:beta)
|
||||
|
||||
# create our temporary directory
|
||||
mkdir $TEMPDIRECTORY
|
||||
|
||||
# extract the files that need to be patched from the services that need to be patched into our temporary directory
|
||||
for COMPONENT in ${COMPONENTS[@]}; do
|
||||
mkdir "$TEMPDIRECTORY/$COMPONENT"
|
||||
docker cp $PATCHINSTANCE:/app/$COMPONENT/Core.dll "$TEMPDIRECTORY/$COMPONENT/Core.dll"
|
||||
done
|
||||
|
||||
# run bitBetter, this applies our patches to the required files
|
||||
docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter
|
||||
|
||||
# create a new image with the patched files
|
||||
docker build . --tag bitwarden-patch --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch"
|
||||
|
||||
# stop and remove our temporary container
|
||||
docker stop 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
|
||||
cat "$PWD/.servers/serverlist.txt" | while read LINE; do
|
||||
bash -c "$LINE"
|
||||
done
|
||||
|
||||
# remove our bitBetter image
|
||||
docker image rm bitbetter/bitbetter
|
||||
|
||||
# build the licenseGen
|
||||
docker build -t bitbetter/licensegen "$PWD/src/licenseGen"
|
||||
|
||||
# clean the licenseGen source directory
|
||||
rm -f "$PWD/src/licenseGen/Core.dll"
|
||||
rm -f "$PWD/src/licenseGen/cert.pfx"
|
22
generateKeys.ps1
Normal file
22
generateKeys.ps1
Normal file
@ -0,0 +1,22 @@
|
||||
# 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.cert`" -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.cert`" -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"
|
@ -1,20 +1,19 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# Check for openssl
|
||||
command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl required but not found. Aborting."; exit 1; }
|
||||
|
||||
DIR=`dirname "$0"`
|
||||
DIR=`exec 2>/dev/null;(cd -- "$DIR") && cd -- "$DIR"|| cd "$DIR"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
|
||||
DIR="$PWD/.keys"
|
||||
|
||||
# Remove any existing key files
|
||||
[ ! -e "$DIR/cert.pem" ] || rm "$DIR/cert.pem"
|
||||
[ ! -e "$DIR/key.pem" ] || rm "$DIR/key.pem"
|
||||
[ ! -e "$DIR/cert.cert" ] || rm "$DIR/cert.cert"
|
||||
[ ! -e "$DIR/cert.pfx" ] || rm "$DIR/cert.pfx"
|
||||
# if previous keys exist, remove them
|
||||
if [ -d "$DIR" ]; then
|
||||
rm -rf "$DIR"
|
||||
fi
|
||||
|
||||
# create new directory
|
||||
mkdir "$DIR"
|
||||
|
||||
# Generate new keys
|
||||
openssl req -x509 -newkey rsa:4096 -keyout "$DIR/key.pem" -out "$DIR/cert.cert" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test
|
||||
openssl x509 -inform DER -in "$DIR/cert.cert" -out "$DIR/cert.pem"
|
||||
openssl pkcs12 -export -out "$DIR/cert.pfx" -inkey "$DIR/key.pem" -in "$DIR/cert.pem" -passin pass:test -passout pass:test
|
||||
|
||||
ls
|
14
licenseGen.ps1
Normal file
14
licenseGen.ps1
Normal file
@ -0,0 +1,14 @@
|
||||
if ($($args.Count) -lt 1) {
|
||||
echo "USAGE: <License Gen action> [License Gen args...]"
|
||||
echo "ACTIONS:"
|
||||
echo " interactive"
|
||||
echo " user"
|
||||
echo " org"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
if ($args[0] = "interactive") {
|
||||
docker run -it --rm bitbetter/licensegen interactive
|
||||
} else {
|
||||
docker run bitbetter/licensegen $args
|
||||
}
|
16
licenseGen.sh
Executable file
16
licenseGen.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "USAGE: <License Gen action> [License Gen args...]"
|
||||
echo "ACTIONS:"
|
||||
echo " interactive"
|
||||
echo " user"
|
||||
echo " org"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$1" = "interactive" ]; then
|
||||
docker run -it --rm bitbetter/licensegen interactive
|
||||
else
|
||||
docker run --rm bitbetter/licensegen "$@"
|
||||
fi
|
@ -1,11 +1,14 @@
|
||||
ARG BITWARDEN_TAG
|
||||
FROM ${BITWARDEN_TAG}
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build
|
||||
WORKDIR /bitBetter
|
||||
|
||||
COPY bin/Debug/netcoreapp6.0/publish/* /bitBetter/
|
||||
COPY ./.keys/cert.cert /newLicensing.cer
|
||||
COPY . /bitBetter
|
||||
COPY cert.cert /app/
|
||||
|
||||
RUN set -e; set -x; \
|
||||
dotnet /bitBetter/bitBetter.dll && \
|
||||
mv /app/Core.dll /app/Core.orig.dll && \
|
||||
mv /app/modified.dll /app/Core.dll && \
|
||||
rm -rf /bitBetter && rm -rf /newLicensing.cer
|
||||
RUN dotnet restore
|
||||
RUN dotnet publish -c Release -o /app --no-restore
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
|
||||
ENTRYPOINT [ "/app/bitBetter" ]
|
4
src/bitBetter/Dockerfile-bitwarden-patch
Normal file
4
src/bitBetter/Dockerfile-bitwarden-patch
Normal file
@ -0,0 +1,4 @@
|
||||
FROM bitwarden/self-host:beta
|
||||
|
||||
COPY ./temp/Api/Core.dll /app/Api/Core.dll
|
||||
COPY ./temp/Identity/Core.dll /app/Identity/Core.dll
|
@ -1,93 +1,75 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Rocks;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet.Writer;
|
||||
using dnlib.IO;
|
||||
|
||||
namespace bitwardenSelfLicensor
|
||||
namespace bitBetter;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
class Program
|
||||
private static Int32 Main(String[] args)
|
||||
{
|
||||
static int Main(string[] args)
|
||||
const String certFile = "/app/cert.cert";
|
||||
String[] files = Directory.GetFiles("/app/mount", "Core.dll", SearchOption.AllDirectories);
|
||||
|
||||
foreach (String file in files)
|
||||
{
|
||||
string cerFile;
|
||||
string corePath;
|
||||
Console.WriteLine(file);
|
||||
ModuleDefMD moduleDefMd = ModuleDefMD.Load(file);
|
||||
Byte[] cert = File.ReadAllBytes(certFile);
|
||||
|
||||
if(args.Length >= 2) {
|
||||
cerFile = args[0];
|
||||
corePath = args[1];
|
||||
} else if (args.Length == 1) {
|
||||
cerFile = args[0];
|
||||
corePath = "/app/Core.dll";
|
||||
}
|
||||
else {
|
||||
cerFile = "/newLicensing.cer";
|
||||
corePath = "/app/Core.dll";
|
||||
}
|
||||
EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources
|
||||
.OfType<EmbeddedResource>()
|
||||
.First(r => r.Name.Equals("Bit.Core.licensing.cer"));
|
||||
|
||||
Console.WriteLine(embeddedResourceToRemove.Name);
|
||||
|
||||
var module = ModuleDefinition.ReadModule(new MemoryStream(File.ReadAllBytes(corePath)));
|
||||
var cert = File.ReadAllBytes(cerFile);
|
||||
EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) {Attributes = embeddedResourceToRemove.Attributes };
|
||||
moduleDefMd.Resources.Add(embeddedResourceToAdd);
|
||||
moduleDefMd.Resources.Remove(embeddedResourceToRemove);
|
||||
|
||||
var x = module.Resources.OfType<EmbeddedResource>()
|
||||
.Where(r => r.Name.Equals("Bit.Core.licensing.cer"))
|
||||
.First();
|
||||
|
||||
Console.WriteLine(x.Name);
|
||||
|
||||
var e = new EmbeddedResource("Bit.Core.licensing.cer", x.Attributes, cert);
|
||||
|
||||
module.Resources.Add(e);
|
||||
module.Resources.Remove(x);
|
||||
|
||||
var services = module.Types.Where(t => t.Namespace == "Bit.Core.Services");
|
||||
DataReader reader = embeddedResourceToRemove.CreateReader();
|
||||
X509Certificate2 existingCert = new(reader.ReadRemainingBytes());
|
||||
|
||||
|
||||
var type = services.First(t => t.Name == "LicensingService");
|
||||
|
||||
var licensingType = type.Resolve();
|
||||
|
||||
var existingCert = new X509Certificate2(x.GetResourceData());
|
||||
|
||||
Console.WriteLine($"Existing Cert Thumbprint: {existingCert.Thumbprint}");
|
||||
X509Certificate2 certificate = new X509Certificate2(cert);
|
||||
X509Certificate2 certificate = new(cert);
|
||||
|
||||
Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}");
|
||||
|
||||
var ctor = licensingType.GetConstructors().Single();
|
||||
|
||||
|
||||
var rewriter = ctor.Body.GetILProcessor();
|
||||
|
||||
var instToReplace =
|
||||
ctor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ldstr
|
||||
&& string.Equals((string)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase))
|
||||
.FirstOrDefault();
|
||||
|
||||
if(instToReplace != null) {
|
||||
rewriter.Replace(instToReplace, Instruction.Create(OpCodes.Ldstr, certificate.Thumbprint));
|
||||
IEnumerable<TypeDef> services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.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("Cant find inst");
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Can't find constructor to patch");
|
||||
}
|
||||
|
||||
// foreach (var inst in ctor.Body.Instructions)
|
||||
// {
|
||||
// Console.Write(inst.OpCode.Name + " " + inst.Operand?.GetType() + " = ");
|
||||
// if(inst.OpCode.FlowControl == FlowControl.Call) {
|
||||
// Console.WriteLine(inst.Operand);
|
||||
// }
|
||||
// else if(inst.OpCode == OpCodes.Ldstr) {
|
||||
// Console.WriteLine(inst.Operand);
|
||||
// }
|
||||
// else {Console.WriteLine();}
|
||||
// }
|
||||
ModuleWriterOptions moduleWriterOptions = new(moduleDefMd);
|
||||
moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.KeepOldMaxStack;
|
||||
moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveAll;
|
||||
moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveRids;
|
||||
|
||||
module.Write("modified.dll");
|
||||
|
||||
return 0;
|
||||
moduleDefMd.Write(file + ".new");
|
||||
moduleDefMd.Dispose();
|
||||
File.Delete(file);
|
||||
File.Move(file + ".new", file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@
|
||||
<TargetFramework>netcoreapp6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dnlib" Version="3.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
dotnet restore
|
||||
dotnet publish
|
@ -1,17 +1,15 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build
|
||||
|
||||
WORKDIR /licenseGen
|
||||
|
||||
COPY . /licenseGen
|
||||
COPY Core.dll /app/
|
||||
COPY cert.pfx /app/
|
||||
|
||||
RUN set -e; set -x; \
|
||||
dotnet add package Newtonsoft.Json --version 13.0.1 \
|
||||
&& dotnet restore \
|
||||
&& dotnet publish
|
||||
RUN dotnet restore
|
||||
RUN dotnet publish -c Release -o /app --no-restore
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
|
||||
FROM bitbetter/api
|
||||
|
||||
COPY --from=build /licenseGen/bin/Debug/netcoreapp6.0/publish/* /app/
|
||||
|
||||
ENTRYPOINT [ "dotnet", "/app/licenseGen.dll", "--core", "/app/Core.dll", "--cert", "/cert.pfx" ]
|
||||
ENTRYPOINT [ "dotnet", "/app/licenseGen.dll", "--core", "/app/Core.dll", "--cert", "/app/cert.pfx" ]
|
||||
|
@ -1,448 +1,445 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace bitwardenSelfLicensor
|
||||
namespace licenseGen;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
class Program
|
||||
private static Int32 Main(String[] args)
|
||||
{
|
||||
static int Main(string[] args)
|
||||
CommandLineApplication app = new();
|
||||
CommandOption cert = app.Option("--cert", "cert file", CommandOptionType.SingleValue);
|
||||
CommandOption coreDll = app.Option("--core", "path to core dll", CommandOptionType.SingleValue);
|
||||
|
||||
Boolean CertExists()
|
||||
{
|
||||
var app = new Microsoft.Extensions.CommandLineUtils.CommandLineApplication();
|
||||
var cert = app.Option("--cert", "cert file", CommandOptionType.SingleValue);
|
||||
var coreDll = app.Option("--core", "path to core dll", CommandOptionType.SingleValue);
|
||||
return File.Exists(cert.Value());
|
||||
}
|
||||
|
||||
bool certExists()
|
||||
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="";
|
||||
Int16 storage = 0;
|
||||
|
||||
Boolean validGuid = false, validInstallid = false;
|
||||
Guid guid = new(), installid = new();
|
||||
|
||||
config.OnExecute(() =>
|
||||
{
|
||||
return File.Exists(cert.Value());
|
||||
}
|
||||
|
||||
bool coreExists()
|
||||
{
|
||||
return File.Exists(coreDll.Value());
|
||||
}
|
||||
|
||||
bool verifyTopOptions()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(cert.Value()) &&
|
||||
!string.IsNullOrWhiteSpace(coreDll.Value()) &&
|
||||
certExists() && coreExists();
|
||||
}
|
||||
|
||||
app.Command("interactive", config =>
|
||||
{
|
||||
string buff="", licensetype="", name="", email="", businessname="";
|
||||
short storage = 0;
|
||||
|
||||
bool valid_guid = false, valid_installid = false;
|
||||
Guid guid = new Guid(), installid = new Guid();
|
||||
|
||||
config.OnExecute(() =>
|
||||
if (!VerifyTopOptions())
|
||||
{
|
||||
if (!verifyTopOptions())
|
||||
if (!CoreExists()) config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}");
|
||||
if (!CertExists()) config.Error.WriteLine($"Cant find certificate at: {cert.Value()}");
|
||||
|
||||
config.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if(buff == "u")
|
||||
{
|
||||
if (!coreExists()) config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}");
|
||||
if (!certExists()) config.Error.WriteLine($"Cant find certificate at: {cert.Value()}");
|
||||
licensetype = "user";
|
||||
Console.WriteLine("Okay, we will generate a user license.");
|
||||
|
||||
config.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
WriteLine("Interactive license mode...");
|
||||
|
||||
while (licensetype == "")
|
||||
{
|
||||
WriteLine("What would you like to generate, a [u]ser license or an [o]rg license?");
|
||||
buff = Console.ReadLine();
|
||||
|
||||
if(buff == "u")
|
||||
while (validGuid == false)
|
||||
{
|
||||
licensetype = "user";
|
||||
WriteLineOver("Okay, we will generate a user license.");
|
||||
Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: ");
|
||||
buff = Console.ReadLine();
|
||||
|
||||
while (valid_guid == false)
|
||||
if (Guid.TryParse(buff, out guid))validGuid = true;
|
||||
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 == "")
|
||||
{
|
||||
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))valid_guid = true;
|
||||
else WriteLineOver("The user-guid provided does not appear to be valid.");
|
||||
businessname = "BitBetter";
|
||||
}
|
||||
else if (CheckBusinessName(buff))
|
||||
{
|
||||
businessname = buff;
|
||||
}
|
||||
}
|
||||
else if (buff == "o")
|
||||
{
|
||||
licensetype = "org";
|
||||
WriteLineOver("Okay, we will generate an organization license.");
|
||||
|
||||
while (valid_installid == false)
|
||||
{
|
||||
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)) valid_installid = true;
|
||||
else WriteLineOver("The install-id provided does not appear to be valid.");
|
||||
}
|
||||
|
||||
while (businessname == "")
|
||||
{
|
||||
WriteLineOver("Please enter a business name, default is BitBetter. [Business Name]:");
|
||||
buff = Console.ReadLine();
|
||||
if (buff == "") businessname = "BitBetter";
|
||||
else if (checkBusinessName(buff)) businessname = buff;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLineOver("Unrecognized option \'" + buff + "\'. ");
|
||||
}
|
||||
}
|
||||
|
||||
while (name == "")
|
||||
else
|
||||
{
|
||||
WriteLineOver("Please provide the username this license will be registered to. [username]:");
|
||||
buff = Console.ReadLine();
|
||||
if ( checkUsername(buff) ) name = buff;
|
||||
Console.WriteLine("Unrecognized option \'" + buff + "\'.");
|
||||
}
|
||||
}
|
||||
|
||||
while (email == "")
|
||||
{
|
||||
WriteLineOver("Please provide the email address for the user " + name + ". [email]");
|
||||
buff = Console.ReadLine();
|
||||
if ( checkEmail(buff) ) email = buff;
|
||||
}
|
||||
while (name == "")
|
||||
{
|
||||
Console.WriteLine("Please provide the username this license will be registered to. [username]: ");
|
||||
buff = Console.ReadLine();
|
||||
if ( CheckUsername(buff) ) name = buff;
|
||||
}
|
||||
|
||||
while (storage == 0)
|
||||
while (email == "")
|
||||
{
|
||||
Console.WriteLine("Please provide the email address for the user " + name + ". [email]: ");
|
||||
buff = Console.ReadLine();
|
||||
if (CheckEmail(buff))
|
||||
{
|
||||
WriteLineOver("Extra storage space for the user " + name + ". (max.: " + short.MaxValue + "). Defaults to maximum value. [storage]");
|
||||
buff = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(buff))
|
||||
{
|
||||
storage = short.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (checkStorage(buff)) storage = short.Parse(buff);
|
||||
}
|
||||
email = buff;
|
||||
}
|
||||
}
|
||||
|
||||
if (licensetype == "user")
|
||||
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))
|
||||
{
|
||||
WriteLineOver("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n");
|
||||
buff = Console.ReadLine();
|
||||
if ( buff == "" || buff == "y" || buff == "Y" )
|
||||
storage = Int16.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CheckStorage(buff))
|
||||
{
|
||||
GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, guid, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLineOver("Exiting...");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (licensetype == "org")
|
||||
{
|
||||
WriteLineOver("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 == "" || buff == "y" || buff == "Y" )
|
||||
{
|
||||
GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, installid, businessname, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLineOver("Exiting...");
|
||||
return 0;
|
||||
storage = Int16.Parse(buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
if (licensetype == "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;
|
||||
}
|
||||
}
|
||||
else if (licensetype == "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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
|
||||
app.Command("user", config =>
|
||||
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(() =>
|
||||
{
|
||||
var name = config.Argument("Name", "your name");
|
||||
var email = config.Argument("Email", "your email");
|
||||
var userIdArg = config.Argument("User ID", "your user id");
|
||||
var storage = config.Argument("Storage", "extra storage space in GB. Maximum is " + short.MaxValue + " (optional, default = max)");
|
||||
var key = config.Argument("Key", "your key id (optional)");
|
||||
var help = config.HelpOption("--help | -h | -?");
|
||||
|
||||
config.OnExecute(() =>
|
||||
if (!VerifyTopOptions())
|
||||
{
|
||||
if (!verifyTopOptions())
|
||||
if (!CoreExists())
|
||||
{
|
||||
if (!coreExists())
|
||||
{
|
||||
config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}");
|
||||
}
|
||||
if (!certExists())
|
||||
{
|
||||
config.Error.WriteLine($"Cant find certificate at: {cert.Value()}");
|
||||
}
|
||||
|
||||
config.ShowHelp();
|
||||
return 1;
|
||||
config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}");
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(name.Value) || string.IsNullOrWhiteSpace(email.Value))
|
||||
if (!CertExists())
|
||||
{
|
||||
config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'");
|
||||
config.ShowHelp("user");
|
||||
return 1;
|
||||
config.Error.WriteLine($"Cant find certificate at: {cert.Value()}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(userIdArg.Value) || !Guid.TryParse(userIdArg.Value, out Guid userId))
|
||||
{
|
||||
config.Error.WriteLine($"User ID not provided");
|
||||
config.ShowHelp("user");
|
||||
return 1;
|
||||
}
|
||||
config.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
short storageShort = 0;
|
||||
if (!string.IsNullOrWhiteSpace(storage.Value))
|
||||
{
|
||||
var parsedStorage = double.Parse(storage.Value);
|
||||
if (parsedStorage > short.MaxValue || parsedStorage < 0)
|
||||
{
|
||||
config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + short.MaxValue + "]");
|
||||
config.ShowHelp("org");
|
||||
return 1;
|
||||
}
|
||||
storageShort = (short) parsedStorage;
|
||||
}
|
||||
|
||||
GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value);
|
||||
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
app.Command("org", config =>
|
||||
{
|
||||
var name = config.Argument("Name", "your name");
|
||||
var email = config.Argument("Email", "your email");
|
||||
var installId = config.Argument("InstallId", "your installation id (GUID)");
|
||||
var storage = config.Argument("Storage", "extra storage space in GB. Maximum is " + short.MaxValue + " (optional, default = max)");
|
||||
var businessName = config.Argument("BusinessName", "name for the organization (optional)");
|
||||
var key = config.Argument("Key", "your key id (optional)");
|
||||
var help = config.HelpOption("--help | -h | -?");
|
||||
|
||||
config.OnExecute(() =>
|
||||
if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value))
|
||||
{
|
||||
if (!verifyTopOptions())
|
||||
{
|
||||
if (!coreExists())
|
||||
{
|
||||
config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}");
|
||||
}
|
||||
if (!certExists())
|
||||
{
|
||||
config.Error.WriteLine($"Cant find certificate at: {cert.Value()}");
|
||||
}
|
||||
config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'");
|
||||
config.ShowHelp("user");
|
||||
return 1;
|
||||
}
|
||||
|
||||
config.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(name.Value) ||
|
||||
string.IsNullOrWhiteSpace(email.Value) ||
|
||||
string.IsNullOrWhiteSpace(installId.Value))
|
||||
if (String.IsNullOrWhiteSpace(userIdArg.Value) || !Guid.TryParse(userIdArg.Value, out Guid userId))
|
||||
{
|
||||
config.Error.WriteLine("User ID not provided");
|
||||
config.ShowHelp("user");
|
||||
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($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}' InstallId='{installId.Value}'");
|
||||
config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]");
|
||||
config.ShowHelp("org");
|
||||
return 1;
|
||||
}
|
||||
storageShort = (Int16) parsedStorage;
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(installId.Value, out Guid installationId))
|
||||
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(() =>
|
||||
{
|
||||
if (!VerifyTopOptions())
|
||||
{
|
||||
if (!CoreExists())
|
||||
{
|
||||
config.Error.WriteLine("Unable to parse your installation id as a GUID");
|
||||
config.Error.WriteLine($"Here's a new guid: {Guid.NewGuid()}");
|
||||
config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}");
|
||||
}
|
||||
if (!CertExists())
|
||||
{
|
||||
config.Error.WriteLine($"Cant find certificate at: {cert.Value()}");
|
||||
}
|
||||
|
||||
config.ShowHelp();
|
||||
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.ShowHelp("org");
|
||||
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("org");
|
||||
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("org");
|
||||
return 1;
|
||||
}
|
||||
storageShort = (Int16) parsedStorage;
|
||||
}
|
||||
|
||||
short storageShort = 0;
|
||||
if (!string.IsNullOrWhiteSpace(storage.Value))
|
||||
{
|
||||
var parsedStorage = double.Parse(storage.Value);
|
||||
if (parsedStorage > short.MaxValue || parsedStorage < 0)
|
||||
{
|
||||
config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + short.MaxValue + "]");
|
||||
config.ShowHelp("org");
|
||||
return 1;
|
||||
}
|
||||
storageShort = (short) 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.ShowHelp();
|
||||
return 10;
|
||||
});
|
||||
|
||||
app.HelpOption("-? | -h | --help");
|
||||
|
||||
try
|
||||
{
|
||||
return app.Execute(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Error.WriteLine("Oops: {0}", e);
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
// checkUsername Checks that the username is a valid username
|
||||
static bool checkUsername(string s)
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
if ( string.IsNullOrWhiteSpace(s) ) {
|
||||
WriteLineOver("The username provided doesn't appear to be valid.\n");
|
||||
return false;
|
||||
}
|
||||
return true; // TODO: Actually validate
|
||||
}
|
||||
app.ShowHelp();
|
||||
return 10;
|
||||
});
|
||||
|
||||
// checkBusinessName Checks that the Business Name is a valid username
|
||||
static bool checkBusinessName(string s)
|
||||
app.HelpOption("-? | -h | --help");
|
||||
|
||||
try
|
||||
{
|
||||
if ( string.IsNullOrWhiteSpace(s) ) {
|
||||
WriteLineOver("The Business Name provided doesn't appear to be valid.\n");
|
||||
return false;
|
||||
}
|
||||
return true; // TODO: Actually validate
|
||||
return app.Execute(args);
|
||||
}
|
||||
|
||||
// checkEmail Checks that the email address is a valid email address
|
||||
static bool checkEmail(string s)
|
||||
catch (Exception e)
|
||||
{
|
||||
if ( string.IsNullOrWhiteSpace(s) ) {
|
||||
WriteLineOver("The email provided doesn't appear to be valid.\n");
|
||||
return false;
|
||||
}
|
||||
return true; // TODO: Actually validate
|
||||
}
|
||||
|
||||
// checkStorage Checks that the storage is in a valid range
|
||||
static bool checkStorage(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
{
|
||||
WriteLineOver("The storage provided doesn't appear to be valid.\n");
|
||||
return false;
|
||||
}
|
||||
if (double.Parse(s) > short.MaxValue || double.Parse(s) < 0)
|
||||
{
|
||||
WriteLineOver("The storage value provided is outside the accepted range of [0-" + short.MaxValue + "].\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// WriteLineOver Writes a new line to console over last line.
|
||||
static void WriteLineOver(string s)
|
||||
{
|
||||
Console.SetCursorPosition(0, Console.CursorTop -1);
|
||||
Console.WriteLine(s);
|
||||
}
|
||||
|
||||
// WriteLine This wrapper is just here so that console writes all look similar.
|
||||
static void WriteLine(string s)
|
||||
{
|
||||
Console.WriteLine(s);
|
||||
}
|
||||
|
||||
static void GenerateUserLicense(X509Certificate2 cert, string corePath, string userName, string email, short storage, Guid userId, string key)
|
||||
{
|
||||
var core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath);
|
||||
|
||||
var type = core.GetType("Bit.Core.Models.Business.UserLicense");
|
||||
var licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType");
|
||||
|
||||
var license = Activator.CreateInstance(type);
|
||||
|
||||
void set(string name, object value)
|
||||
{
|
||||
type.GetProperty(name).SetValue(license, value);
|
||||
}
|
||||
|
||||
set("LicenseKey", string.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key);
|
||||
set("Id", userId);
|
||||
set("Name", userName);
|
||||
set("Email", email);
|
||||
set("Premium", true);
|
||||
set("MaxStorageGb", storage == 0 ? short.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, new object[0])));
|
||||
set("Signature", Convert.ToBase64String((byte[])type.GetMethod("Sign").Invoke(license, new object[] { cert })));
|
||||
|
||||
Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
}
|
||||
|
||||
static void GenerateOrgLicense(X509Certificate2 cert, string corePath, string userName, string email, short storage, Guid instalId, string businessName, string key)
|
||||
{
|
||||
var core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath);
|
||||
|
||||
var type = core.GetType("Bit.Core.Models.Business.OrganizationLicense");
|
||||
var licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType");
|
||||
var planTypeEnum = core.GetType("Bit.Core.Enums.PlanType");
|
||||
|
||||
var license = Activator.CreateInstance(type);
|
||||
|
||||
void set(string name, object value)
|
||||
{
|
||||
type.GetProperty(name).SetValue(license, value);
|
||||
}
|
||||
|
||||
set("LicenseKey", string.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key);
|
||||
set("InstallationId", instalId);
|
||||
set("Id", Guid.NewGuid());
|
||||
set("Name", userName);
|
||||
set("BillingEmail", email);
|
||||
set("BusinessName", string.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName);
|
||||
set("Enabled", true);
|
||||
set("Plan", "Custom");
|
||||
set("PlanType", Enum.Parse(planTypeEnum, "Custom"));
|
||||
set("Seats", (int)short.MaxValue);
|
||||
set("MaxCollections", short.MaxValue);
|
||||
set("UsePolicies", true);
|
||||
set("UseSso", true);
|
||||
set("UseKeyConnector", true);
|
||||
//set("UseScim", true); // available in version 10, which is not released yet
|
||||
set("UseGroups", true);
|
||||
set("UseEvents", true);
|
||||
set("UseDirectory", true);
|
||||
set("UseTotp", true);
|
||||
set("Use2fa", true);
|
||||
set("UseApi", true);
|
||||
set("UseResetPassword", true);
|
||||
set("MaxStorageGb", storage == 0 ? short.MaxValue : storage);
|
||||
set("SelfHost", true);
|
||||
set("UsersGetPremium", true);
|
||||
set("Version", 9);
|
||||
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("Hash", Convert.ToBase64String((byte[])type.GetMethod("ComputeHash").Invoke(license, new object[0])));
|
||||
set("Signature", Convert.ToBase64String((byte[])type.GetMethod("Sign").Invoke(license, new object[] { cert })));
|
||||
|
||||
Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
Console.Error.WriteLine("Oops: {0}", e);
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key)
|
||||
{
|
||||
Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath);
|
||||
|
||||
Type type = core.GetType("Bit.Core.Models.Business.UserLicense");
|
||||
Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType");
|
||||
|
||||
Object license = Activator.CreateInstance(type);
|
||||
|
||||
void Set(String name, Object value)
|
||||
{
|
||||
type.GetProperty(name).SetValue(license, value);
|
||||
}
|
||||
|
||||
Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key);
|
||||
Set("Id", userId);
|
||||
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, new Object[0])));
|
||||
Set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, new Object[] { cert })));
|
||||
|
||||
Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Type type = core.GetType("Bit.Core.Models.Business.OrganizationLicense");
|
||||
Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType");
|
||||
Type planTypeEnum = core.GetType("Bit.Core.Enums.PlanType");
|
||||
|
||||
Object license = Activator.CreateInstance(type);
|
||||
|
||||
void set(String name, Object value)
|
||||
{
|
||||
type.GetProperty(name).SetValue(license, value);
|
||||
}
|
||||
|
||||
set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key);
|
||||
set("InstallationId", instalId);
|
||||
set("Id", Guid.NewGuid());
|
||||
set("Name", userName);
|
||||
set("BillingEmail", email);
|
||||
set("BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName);
|
||||
set("Enabled", true);
|
||||
set("Plan", "Custom");
|
||||
set("PlanType", Enum.Parse(planTypeEnum, "Custom"));
|
||||
set("Seats", (Int32)Int16.MaxValue);
|
||||
set("MaxCollections", Int16.MaxValue);
|
||||
set("UsePolicies", true);
|
||||
set("UseSso", true);
|
||||
set("UseKeyConnector", true);
|
||||
//set("UseScim", true); // available in version 10, which is not released yet
|
||||
set("UseGroups", true);
|
||||
set("UseEvents", true);
|
||||
set("UseDirectory", true);
|
||||
set("UseTotp", true);
|
||||
set("Use2fa", true);
|
||||
set("UseApi", true);
|
||||
set("UseResetPassword", true);
|
||||
set("MaxStorageGb", storage == 0 ? Int16.MaxValue : storage);
|
||||
set("SelfHost", true);
|
||||
set("UsersGetPremium", true);
|
||||
set("Version", 9);
|
||||
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("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, new Object[0])));
|
||||
set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, new Object[] { cert })));
|
||||
|
||||
Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=`dirname "$0"`
|
||||
DIR=`exec 2>/dev/null;(cd -- "$DIR") && cd -- "$DIR"|| cd "$DIR"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
|
||||
|
||||
docker build -t bitbetter/licensegen "$DIR" # --squash
|
@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=`dirname "$0"`
|
||||
DIR=`exec 2>/dev/null;(cd -- "$DIR") && cd -- "$DIR"|| cd "$DIR"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
|
||||
|
||||
# Grab the absolute path to the default pfx location
|
||||
cert_path="$DIR/../../.keys/cert.pfx"
|
||||
|
||||
if [ "$#" -lt "2" ]; then
|
||||
echo "USAGE: $0 <ABSOLUTE PATH TO CERT.PFX> <License Gen action> [License Gen args...]"
|
||||
echo "ACTIONS:"
|
||||
echo " interactive"
|
||||
echo " user"
|
||||
echo " org"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cert_path="$1"
|
||||
action="$2"
|
||||
shift
|
||||
|
||||
if [ $action = "interactive" ]; then
|
||||
docker run -it --rm -v "$cert_path:/cert.pfx" bitbetter/licensegen "$@"
|
||||
else
|
||||
docker run --rm -v "$cert_path:/cert.pfx" bitbetter/licensegen "$@"
|
||||
fi
|
@ -1,85 +0,0 @@
|
||||
#!/bin/bash
|
||||
ask () {
|
||||
local __resultVar=$1
|
||||
local __result="$2"
|
||||
if [ -z "$2" ]; then
|
||||
read -p "$3" __result
|
||||
fi
|
||||
eval $__resultVar="'$__result'"
|
||||
}
|
||||
|
||||
SCRIPT_BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
BW_VERSION=$(curl -sL https://go.btwrdn.co/bw-sh-versions | grep '^ *"'coreVersion'":' | awk -F\: '{ print $2 }' | sed -e 's/,$//' -e 's/^"//' -e 's/"$//')
|
||||
|
||||
echo "Starting Bitwarden update, newest server version: $BW_VERSION"
|
||||
|
||||
# Default path is the parent directory of the BitBetter location
|
||||
BITWARDEN_BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null 2>&1 && pwd )"
|
||||
|
||||
# Get Bitwarden base from user (or keep default value) or use first argument
|
||||
ask tmpbase "$1" "Enter Bitwarden base directory [$BITWARDEN_BASE]: "
|
||||
BITWARDEN_BASE=${tmpbase:-$BITWARDEN_BASE}
|
||||
|
||||
# Check if directory exists and is valid
|
||||
[ -d "$BITWARDEN_BASE" ] || { echo "Bitwarden base directory $BITWARDEN_BASE not found!"; exit 1; }
|
||||
[ -f "$BITWARDEN_BASE/bitwarden.sh" ] || { echo "Bitwarden base directory $BITWARDEN_BASE is not valid!"; exit 1; }
|
||||
|
||||
# Check if user wants to recreate the docker-compose override file
|
||||
RECREATE_OV="y"
|
||||
ask tmprecreate "$2" "Rebuild docker-compose override? [Y/n]: "
|
||||
RECREATE_OV=${tmprecreate:-$RECREATE_OV}
|
||||
|
||||
if [[ $RECREATE_OV =~ ^[Yy]$ ]]
|
||||
then
|
||||
{
|
||||
echo "version: '3'"
|
||||
echo ""
|
||||
echo "services:"
|
||||
echo " api:"
|
||||
echo " image: bitbetter/api:$BW_VERSION"
|
||||
echo ""
|
||||
echo " identity:"
|
||||
echo " image: bitbetter/identity:$BW_VERSION"
|
||||
echo ""
|
||||
} > $BITWARDEN_BASE/bwdata/docker/docker-compose.override.yml
|
||||
echo "BitBetter docker-compose override created!"
|
||||
else
|
||||
echo "Make sure to check if the docker override contains the correct image version ($BW_VERSION) in $BITWARDEN_BASE/bwdata/docker/docker-compose.override.yml!"
|
||||
fi
|
||||
|
||||
# Check if user wants to rebuild the bitbetter images
|
||||
docker images bitbetter/api --format="{{ .Tag }}" | grep -F -- "${BW_VERSION}" > /dev/null
|
||||
retval=$?
|
||||
REBUILD_BB="n"
|
||||
REBUILD_BB_DESCR="[y/N]"
|
||||
if [ $retval -ne 0 ]; then
|
||||
REBUILD_BB="y"
|
||||
REBUILD_BB_DESCR="[Y/n]"
|
||||
fi
|
||||
ask tmprebuild "$3" "Rebuild BitBetter images? $REBUILD_BB_DESCR: "
|
||||
REBUILD_BB=${tmprebuild:-$REBUILD_BB}
|
||||
|
||||
if [[ $REBUILD_BB =~ ^[Yy]$ ]]
|
||||
then
|
||||
$SCRIPT_BASE/build.sh
|
||||
echo "BitBetter images updated to version: $BW_VERSION"
|
||||
fi
|
||||
|
||||
# Now start the bitwarden update
|
||||
cd $BITWARDEN_BASE
|
||||
|
||||
./bitwarden.sh updateself
|
||||
|
||||
# Update the bitwarden.sh: automatically patch run.sh to fix docker-compose pull errors for private images
|
||||
awk '1;/function downloadRunFile/{c=6}c&&!--c{print "sed -i '\''s/dccmd pull/dccmd pull --ignore-pull-failures || true/g'\'' $SCRIPTS_DIR/run.sh"}' $BITWARDEN_BASE/bitwarden.sh > tmp_bw.sh && mv tmp_bw.sh $BITWARDEN_BASE/bitwarden.sh
|
||||
chmod +x $BITWARDEN_BASE/bitwarden.sh
|
||||
echo "Patching bitwarden.sh completed..."
|
||||
|
||||
./bitwarden.sh update
|
||||
|
||||
# Prune Docker images without at least one container associated to them.
|
||||
echo "Pruning Docker images without at least one container associated to them..."
|
||||
docker image prune -a
|
||||
|
||||
cd $SCRIPT_BASE
|
||||
echo "Bitwarden update completed!"
|
Loading…
Reference in New Issue
Block a user