mirror of
				https://github.com/jakeswenson/BitBetter.git
				synced 2025-10-31 04:43:25 +00: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,4 +1,4 @@ | |||
| version: 2 | ||||
| version: 2.1 | ||||
| jobs: | ||||
|   build: | ||||
|     machine: true | ||||
|  | @ -9,7 +9,7 @@ jobs: | |||
|           command: date | ||||
|       - run: | ||||
|           name: Generate Keys | ||||
|           command: ./.keys/generate-keys.sh | ||||
|           command: ./generateKeys.sh | ||||
|       - run: | ||||
|           name: Build script | ||||
|           command: ./build.sh | ||||
|           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"); | ||||
|              | ||||
| 
 | ||||
|             var type = services.First(t => t.Name == "LicensingService"); | ||||
| 
 | ||||
|             var licensingType =  type.Resolve(); | ||||
| 
 | ||||
|             var existingCert = new X509Certificate2(x.GetResourceData()); | ||||
|             DataReader reader = embeddedResourceToRemove.CreateReader(); | ||||
|             X509Certificate2 existingCert = new(reader.ReadRemainingBytes()); | ||||
|              | ||||
|             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(); | ||||
|             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)); | ||||
|              | ||||
|             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)); | ||||
|             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"); | ||||
|             moduleDefMd.Write(file + ".new"); | ||||
|             moduleDefMd.Dispose(); | ||||
|             File.Delete(file); | ||||
|             File.Move(file + ".new", file); | ||||
|         } | ||||
| 
 | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
|   </PropertyGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Mono.Cecil" Version="0.11.2" /> | ||||
|     <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,159 +1,171 @@ | |||
| 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 | ||||
| { | ||||
|     class Program | ||||
|     { | ||||
|         static int Main(string[] args) | ||||
|         { | ||||
|             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); | ||||
| namespace licenseGen; | ||||
| 
 | ||||
|             bool certExists() | ||||
| internal class Program | ||||
| { | ||||
|     private static Int32 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() | ||||
|         { | ||||
|             return File.Exists(cert.Value()); | ||||
|         } | ||||
| 
 | ||||
|             bool coreExists() | ||||
|         Boolean CoreExists() | ||||
|         { | ||||
|             return File.Exists(coreDll.Value()); | ||||
|         } | ||||
| 
 | ||||
|             bool verifyTopOptions() | ||||
|         Boolean VerifyTopOptions() | ||||
|         { | ||||
|                 return !string.IsNullOrWhiteSpace(cert.Value()) && | ||||
|                        !string.IsNullOrWhiteSpace(coreDll.Value()) && | ||||
|                        certExists() && coreExists(); | ||||
|             return !String.IsNullOrWhiteSpace(cert.Value()) && | ||||
|                    !String.IsNullOrWhiteSpace(coreDll.Value()) && | ||||
|                    CertExists() && CoreExists(); | ||||
|         } | ||||
| 
 | ||||
|         app.Command("interactive", config => | ||||
|         { | ||||
|                 string buff="", licensetype="", name="", email="", businessname=""; | ||||
|                 short storage = 0; | ||||
|             String buff, licensetype="", name="", email="", businessname=""; | ||||
|             Int16 storage = 0; | ||||
| 
 | ||||
|                 bool valid_guid = false, valid_installid = false; | ||||
|                 Guid guid = new Guid(), installid = new Guid(); | ||||
|             Boolean validGuid = false, validInstallid = false; | ||||
|             Guid guid = new(), installid = new(); | ||||
| 
 | ||||
|             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()}"); | ||||
|                     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; | ||||
|                 } | ||||
| 
 | ||||
|                     WriteLine("Interactive license mode..."); | ||||
|                 Console.WriteLine("Interactive license mode..."); | ||||
|                  | ||||
|                 while (licensetype == "") | ||||
|                 { | ||||
|                         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(); | ||||
| 
 | ||||
|                     if(buff == "u") | ||||
|                     { | ||||
|                         licensetype = "user"; | ||||
|                             WriteLineOver("Okay, we will generate a user license."); | ||||
|                         Console.WriteLine("Okay, we will generate a user license."); | ||||
| 
 | ||||
|                             while (valid_guid == false) | ||||
|                         while (validGuid == false) | ||||
|                         { | ||||
|                                 WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]:"); | ||||
|                             Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); | ||||
|                             buff = Console.ReadLine(); | ||||
| 
 | ||||
|                                 if (Guid.TryParse(buff, out guid))valid_guid = true; | ||||
|                                 else WriteLineOver("The user-guid provided does not appear to be valid."); | ||||
|                             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"; | ||||
|                             WriteLineOver("Okay, we will generate an organization license."); | ||||
|                         Console.WriteLine("Okay, we will generate an organization license."); | ||||
| 
 | ||||
|                             while (valid_installid == false) | ||||
|                         while (validInstallid == false) | ||||
|                         { | ||||
|                                 WriteLine("Please provide your Bitwarden Install-ID — refer to the Readme for details on how to retrieve this. [Install-ID]:"); | ||||
|                             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)) valid_installid = true; | ||||
|                                 else WriteLineOver("The install-id provided does not appear to be valid."); | ||||
|                             if (Guid.TryParse(buff, out installid)) validInstallid = true; | ||||
|                             else Console.WriteLine("The install-id provided does not appear to be valid."); | ||||
|                         } | ||||
| 
 | ||||
|                         while (businessname == "") | ||||
|                         { | ||||
|                                 WriteLineOver("Please enter a business name, default is BitBetter. [Business Name]:"); | ||||
|                             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; | ||||
|                             if (buff == "") | ||||
|                             { | ||||
|                                 businessname = "BitBetter"; | ||||
|                             } | ||||
|                             else if (CheckBusinessName(buff)) | ||||
|                             { | ||||
|                                 businessname = buff; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                             WriteLineOver("Unrecognized option \'" + buff + "\'. "); | ||||
|                         Console.WriteLine("Unrecognized option \'" + buff + "\'."); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 while (name == "") | ||||
|                 { | ||||
|                         WriteLineOver("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(); | ||||
|                         if ( checkUsername(buff) )   name = buff; | ||||
|                     if ( CheckUsername(buff) )   name = buff; | ||||
|                 } | ||||
| 
 | ||||
|                 while (email == "") | ||||
|                 { | ||||
|                         WriteLineOver("Please provide the email address for the user " + name + ". [email]"); | ||||
|                     Console.WriteLine("Please provide the email address for the user " + name + ". [email]: "); | ||||
|                     buff = Console.ReadLine(); | ||||
|                         if ( checkEmail(buff) )   email = buff; | ||||
|                     if (CheckEmail(buff)) | ||||
|                     { | ||||
|                         email = buff; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 while (storage == 0) | ||||
|                 { | ||||
|                         WriteLineOver("Extra storage space for the user " + name + ". (max.: " + short.MaxValue + "). Defaults to maximum value. [storage]"); | ||||
|                     Console.WriteLine("Extra storage space for the user " + name + ". (max.: " + Int16.MaxValue + "). Defaults to maximum value. [storage]"); | ||||
|                     buff = Console.ReadLine(); | ||||
|                         if (string.IsNullOrWhiteSpace(buff)) | ||||
|                     if (String.IsNullOrWhiteSpace(buff)) | ||||
|                     { | ||||
|                             storage = short.MaxValue; | ||||
|                         storage = Int16.MaxValue; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                             if (checkStorage(buff)) storage = short.Parse(buff); | ||||
|                         if (CheckStorage(buff)) | ||||
|                         { | ||||
|                             storage = Int16.Parse(buff); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (licensetype == "user") | ||||
|                 { | ||||
|                         WriteLineOver("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n"); | ||||
|                     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 == "" || buff == "y" || buff == "Y" ) | ||||
|                     if ( buff is "" or "y" or "Y" ) | ||||
|                     { | ||||
|                         GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, guid, null); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                             WriteLineOver("Exiting..."); | ||||
|                         Console.WriteLine("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"); | ||||
|                     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 == "" || buff == "y" || buff == "Y" ) | ||||
|                     if ( buff is "" or "y" or "Y" ) | ||||
|                     { | ||||
|                         GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, installid, businessname, null); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                             WriteLineOver("Exiting..."); | ||||
|                         Console.WriteLine("Exiting..."); | ||||
|                         return 0; | ||||
|                     } | ||||
|                 } | ||||
|  | @ -164,22 +176,21 @@ namespace bitwardenSelfLicensor | |||
| 
 | ||||
|         app.Command("user", config => | ||||
|         { | ||||
|                 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 | -?"); | ||||
|             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(() => | ||||
|             { | ||||
|                     if (!verifyTopOptions()) | ||||
|                 if (!VerifyTopOptions()) | ||||
|                 { | ||||
|                         if (!coreExists()) | ||||
|                     if (!CoreExists()) | ||||
|                     { | ||||
|                         config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}"); | ||||
|                     } | ||||
|                         if (!certExists()) | ||||
|                     if (!CertExists()) | ||||
|                     { | ||||
|                         config.Error.WriteLine($"Cant find certificate at: {cert.Value()}"); | ||||
|                     } | ||||
|  | @ -187,31 +198,32 @@ namespace bitwardenSelfLicensor | |||
|                     config.ShowHelp(); | ||||
|                     return 1; | ||||
|                 } | ||||
|                     else if (string.IsNullOrWhiteSpace(name.Value) || string.IsNullOrWhiteSpace(email.Value)) | ||||
| 
 | ||||
|                 if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value)) | ||||
|                 { | ||||
|                     config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'"); | ||||
|                     config.ShowHelp("user"); | ||||
|                     return 1; | ||||
|                 } | ||||
| 
 | ||||
|                     if (string.IsNullOrWhiteSpace(userIdArg.Value) || !Guid.TryParse(userIdArg.Value, out Guid userId)) | ||||
|                 if (String.IsNullOrWhiteSpace(userIdArg.Value) || !Guid.TryParse(userIdArg.Value, out Guid userId)) | ||||
|                 { | ||||
|                         config.Error.WriteLine($"User ID not provided"); | ||||
|                     config.Error.WriteLine("User ID not provided"); | ||||
|                     config.ShowHelp("user"); | ||||
|                     return 1; | ||||
|                 } | ||||
| 
 | ||||
|                     short storageShort = 0; | ||||
|                     if (!string.IsNullOrWhiteSpace(storage.Value)) | ||||
|                 Int16 storageShort = 0; | ||||
|                 if (!String.IsNullOrWhiteSpace(storage.Value)) | ||||
|                 { | ||||
|                         var parsedStorage = double.Parse(storage.Value); | ||||
|                         if (parsedStorage > short.MaxValue || parsedStorage < 0) | ||||
|                     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-" + short.MaxValue + "]"); | ||||
|                         config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]"); | ||||
|                         config.ShowHelp("org"); | ||||
|                         return 1; | ||||
|                     } | ||||
|                         storageShort = (short) parsedStorage; | ||||
|                     storageShort = (Int16) parsedStorage; | ||||
|                 } | ||||
| 
 | ||||
|                 GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value); | ||||
|  | @ -221,23 +233,22 @@ namespace bitwardenSelfLicensor | |||
|         }); | ||||
|         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 | -?"); | ||||
|             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 (!VerifyTopOptions()) | ||||
|                 { | ||||
|                         if (!coreExists()) | ||||
|                     if (!CoreExists()) | ||||
|                     { | ||||
|                         config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}"); | ||||
|                     } | ||||
|                         if (!certExists()) | ||||
|                     if (!CertExists()) | ||||
|                     { | ||||
|                         config.Error.WriteLine($"Cant find certificate at: {cert.Value()}"); | ||||
|                     } | ||||
|  | @ -245,9 +256,10 @@ namespace bitwardenSelfLicensor | |||
|                     config.ShowHelp(); | ||||
|                     return 1; | ||||
|                 } | ||||
|                     else if (string.IsNullOrWhiteSpace(name.Value) || | ||||
|                             string.IsNullOrWhiteSpace(email.Value) || | ||||
|                             string.IsNullOrWhiteSpace(installId.Value)) | ||||
| 
 | ||||
|                 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"); | ||||
|  | @ -262,17 +274,17 @@ namespace bitwardenSelfLicensor | |||
|                     return 1; | ||||
|                 } | ||||
| 
 | ||||
|                     short storageShort = 0; | ||||
|                     if (!string.IsNullOrWhiteSpace(storage.Value)) | ||||
|                 Int16 storageShort = 0; | ||||
|                 if (!String.IsNullOrWhiteSpace(storage.Value)) | ||||
|                 { | ||||
|                         var parsedStorage = double.Parse(storage.Value); | ||||
|                         if (parsedStorage > short.MaxValue || parsedStorage < 0) | ||||
|                     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-" + short.MaxValue + "]"); | ||||
|                         config.Error.WriteLine("The storage value provided is outside the accepted range of [0-" + Int16.MaxValue + "]"); | ||||
|                         config.ShowHelp("org"); | ||||
|                         return 1; | ||||
|                     } | ||||
|                         storageShort = (short) parsedStorage; | ||||
|                     storageShort = (Int16) parsedStorage; | ||||
|                 } | ||||
| 
 | ||||
|                 GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value); | ||||
|  | @ -301,123 +313,109 @@ namespace bitwardenSelfLicensor | |||
|     } | ||||
| 
 | ||||
|     // checkUsername Checks that the username is a valid username | ||||
|         static bool checkUsername(string s) | ||||
|     private static Boolean CheckUsername(String s) | ||||
|     { | ||||
|             if ( string.IsNullOrWhiteSpace(s) ) { | ||||
|                 WriteLineOver("The username provided doesn't appear to be valid.\n"); | ||||
|         // TODO: Actually validate | ||||
|         if (!String.IsNullOrWhiteSpace(s)) return true; | ||||
| 
 | ||||
|         Console.WriteLine("The username provided doesn't appear to be valid!"); | ||||
|         return false; | ||||
|     } | ||||
|             return true;    // TODO: Actually validate | ||||
|         } | ||||
| 
 | ||||
|     // checkBusinessName Checks that the Business Name is a valid username | ||||
|         static bool checkBusinessName(string s) | ||||
|     private static Boolean CheckBusinessName(String s) | ||||
|     { | ||||
|             if ( string.IsNullOrWhiteSpace(s) ) { | ||||
|                 WriteLineOver("The Business Name provided doesn't appear to be valid.\n"); | ||||
|         // TODO: Actually validate | ||||
|         if (!String.IsNullOrWhiteSpace(s)) return true; | ||||
| 
 | ||||
|         Console.WriteLine("The Business Name provided doesn't appear to be valid!"); | ||||
|         return false; | ||||
|     } | ||||
|             return true;    // TODO: Actually validate | ||||
|         } | ||||
| 
 | ||||
|     // checkEmail Checks that the email address is a valid email address | ||||
|         static bool checkEmail(string s) | ||||
|     private static Boolean CheckEmail(String s) | ||||
|     { | ||||
|             if ( string.IsNullOrWhiteSpace(s) ) { | ||||
|                 WriteLineOver("The email provided doesn't appear to be valid.\n"); | ||||
|         // TODO: Actually validate | ||||
|         if (!String.IsNullOrWhiteSpace(s)) return true; | ||||
| 
 | ||||
|         Console.WriteLine("The email provided doesn't appear to be valid!"); | ||||
|         return false; | ||||
|     } | ||||
|             return true;    // TODO: Actually validate | ||||
|         } | ||||
| 
 | ||||
|     // checkStorage Checks that the storage is in a valid range | ||||
|         static bool checkStorage(string s) | ||||
|     private static Boolean CheckStorage(String s) | ||||
|     { | ||||
|             if (string.IsNullOrWhiteSpace(s)) | ||||
|         if (String.IsNullOrWhiteSpace(s)) | ||||
|         { | ||||
|                 WriteLineOver("The storage provided doesn't appear to be valid.\n"); | ||||
|             Console.WriteLine("The storage provided doesn't appear to be valid!"); | ||||
|             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"); | ||||
| 
 | ||||
|         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; | ||||
|     } | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         // WriteLineOver Writes a new line to console over last line. | ||||
|         static void WriteLineOver(string s) | ||||
|     private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key) | ||||
|     { | ||||
|             Console.SetCursorPosition(0, Console.CursorTop -1); | ||||
|             Console.WriteLine(s); | ||||
|         } | ||||
|         Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); | ||||
| 
 | ||||
|         // WriteLine This wrapper is just here so that console writes all look similar. | ||||
|         static void WriteLine(string s) | ||||
|         { | ||||
|             Console.WriteLine(s); | ||||
|         } | ||||
|         Type type = core.GetType("Bit.Core.Models.Business.UserLicense"); | ||||
|         Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); | ||||
| 
 | ||||
|         static void GenerateUserLicense(X509Certificate2 cert, string corePath, string userName, string email, short storage, Guid userId, string key) | ||||
|         { | ||||
|             var core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); | ||||
|         Object license = Activator.CreateInstance(type); | ||||
| 
 | ||||
|             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) | ||||
|         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("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 }))); | ||||
|         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) | ||||
|     private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) | ||||
|     { | ||||
|             var core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); | ||||
|         Assembly 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"); | ||||
|         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"); | ||||
| 
 | ||||
|             var license = Activator.CreateInstance(type); | ||||
|         Object license = Activator.CreateInstance(type); | ||||
| 
 | ||||
|             void set(string name, object value) | ||||
|         void set(String name, Object value) | ||||
|         { | ||||
|             type.GetProperty(name).SetValue(license, value); | ||||
|         } | ||||
| 
 | ||||
|             set("LicenseKey", string.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); | ||||
|         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("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("Seats", (Int32)Int16.MaxValue); | ||||
|         set("MaxCollections", Int16.MaxValue); | ||||
|         set("UsePolicies", true); | ||||
|         set("UseSso", true); | ||||
|         set("UseKeyConnector", true); | ||||
|  | @ -429,7 +427,7 @@ namespace bitwardenSelfLicensor | |||
|         set("Use2fa", true); | ||||
|         set("UseApi", true); | ||||
|         set("UseResetPassword", true); | ||||
|             set("MaxStorageGb", storage == 0 ? short.MaxValue : storage); | ||||
|         set("MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); | ||||
|         set("SelfHost", true); | ||||
|         set("UsersGetPremium", true); | ||||
|         set("Version", 9); | ||||
|  | @ -439,10 +437,9 @@ namespace bitwardenSelfLicensor | |||
|         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 }))); | ||||
|         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