mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 05:41:13 +00:00 
			
		
		
		
	Merge branch 'main' into feature/kdf-options
This commit is contained in:
		
				commit
				
					
						5bcee24f88
					
				
			
		
					 55 changed files with 831 additions and 463 deletions
				
			
		
							
								
								
									
										5
									
								
								.github/workflows/release.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/release.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -48,7 +48,10 @@ jobs: | ||||||
|         ports: |         ports: | ||||||
|           - 5000:5000 |           - 5000:5000 | ||||||
|     env: |     env: | ||||||
|       DOCKER_BUILDKIT: 1 # Disabled for now, but we should look at this because it will speedup building! |       # Use BuildKit (https://docs.docker.com/build/buildkit/) for better | ||||||
|  |       # build performance and the ability to copy extended file attributes | ||||||
|  |       # (e.g., for executable capabilities) across build phases. | ||||||
|  |       DOCKER_BUILDKIT: 1 | ||||||
|       # DOCKER_REPO/secrets.DOCKERHUB_REPO needs to be 'index.docker.io/<user>/<repo>' |       # DOCKER_REPO/secrets.DOCKERHUB_REPO needs to be 'index.docker.io/<user>/<repo>' | ||||||
|       DOCKER_REPO: ${{ secrets.DOCKERHUB_REPO }} |       DOCKER_REPO: ${{ secrets.DOCKERHUB_REPO }} | ||||||
|       SOURCE_COMMIT: ${{ github.sha }} |       SOURCE_COMMIT: ${{ github.sha }} | ||||||
|  |  | ||||||
|  | @ -3,5 +3,7 @@ ignored: | ||||||
|   - DL3008 |   - DL3008 | ||||||
|   # disable explicit version for apk install |   # disable explicit version for apk install | ||||||
|   - DL3018 |   - DL3018 | ||||||
|  |   # disable check for consecutive `RUN` instructions | ||||||
|  |   - DL3059 | ||||||
| trustedRegistries: | trustedRegistries: | ||||||
|   - docker.io |   - docker.io | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ resolver = "2" | ||||||
| 
 | 
 | ||||||
| repository = "https://github.com/dani-garcia/vaultwarden" | repository = "https://github.com/dani-garcia/vaultwarden" | ||||||
| readme = "README.md" | readme = "README.md" | ||||||
| license = "GPL-3.0-only" | license = "AGPL-3.0-only" | ||||||
| publish = false | publish = false | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										143
									
								
								LICENSE.txt
									
										
									
									
									
								
							
							
						
						
									
										143
									
								
								LICENSE.txt
									
										
									
									
									
								
							|  | @ -1,5 +1,5 @@ | ||||||
|                     GNU GENERAL PUBLIC LICENSE |                     GNU AFFERO GENERAL PUBLIC LICENSE | ||||||
|                        Version 3, 29 June 2007 |                        Version 3, 19 November 2007 | ||||||
| 
 | 
 | ||||||
|  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> |  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> | ||||||
|  Everyone is permitted to copy and distribute verbatim copies |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  | @ -7,17 +7,15 @@ | ||||||
| 
 | 
 | ||||||
|                             Preamble |                             Preamble | ||||||
| 
 | 
 | ||||||
|   The GNU General Public License is a free, copyleft license for |   The GNU Affero General Public License is a free, copyleft license for | ||||||
| software and other kinds of works. | software and other kinds of works, specifically designed to ensure | ||||||
|  | cooperation with the community in the case of network server software. | ||||||
| 
 | 
 | ||||||
|   The licenses for most software and other practical works are designed |   The licenses for most software and other practical works are designed | ||||||
| to take away your freedom to share and change the works.  By contrast, | to take away your freedom to share and change the works.  By contrast, | ||||||
| the GNU General Public License is intended to guarantee your freedom to | our General Public Licenses are intended to guarantee your freedom to | ||||||
| share and change all versions of a program--to make sure it remains free | share and change all versions of a program--to make sure it remains free | ||||||
| software for all its users.  We, the Free Software Foundation, use the | software for all its users. | ||||||
| GNU General Public License for most of our software; it applies also to |  | ||||||
| any other work released this way by its authors.  You can apply it to |  | ||||||
| your programs, too. |  | ||||||
| 
 | 
 | ||||||
|   When we speak of free software, we are referring to freedom, not |   When we speak of free software, we are referring to freedom, not | ||||||
| price.  Our General Public Licenses are designed to make sure that you | price.  Our General Public Licenses are designed to make sure that you | ||||||
|  | @ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you | ||||||
| want it, that you can change the software or use pieces of it in new | want it, that you can change the software or use pieces of it in new | ||||||
| free programs, and that you know you can do these things. | free programs, and that you know you can do these things. | ||||||
| 
 | 
 | ||||||
|   To protect your rights, we need to prevent others from denying you |   Developers that use our General Public Licenses protect your rights | ||||||
| these rights or asking you to surrender the rights.  Therefore, you have | with two steps: (1) assert copyright on the software, and (2) offer | ||||||
| certain responsibilities if you distribute copies of the software, or if | you this License which gives you legal permission to copy, distribute | ||||||
| you modify it: responsibilities to respect the freedom of others. | and/or modify the software. | ||||||
| 
 | 
 | ||||||
|   For example, if you distribute copies of such a program, whether |   A secondary benefit of defending all users' freedom is that | ||||||
| gratis or for a fee, you must pass on to the recipients the same | improvements made in alternate versions of the program, if they | ||||||
| freedoms that you received.  You must make sure that they, too, receive | receive widespread use, become available for other developers to | ||||||
| or can get the source code.  And you must show them these terms so they | incorporate.  Many developers of free software are heartened and | ||||||
| know their rights. | encouraged by the resulting cooperation.  However, in the case of | ||||||
|  | software used on network servers, this result may fail to come about. | ||||||
|  | The GNU General Public License permits making a modified version and | ||||||
|  | letting the public access it on a server without ever releasing its | ||||||
|  | source code to the public. | ||||||
| 
 | 
 | ||||||
|   Developers that use the GNU GPL protect your rights with two steps: |   The GNU Affero General Public License is designed specifically to | ||||||
| (1) assert copyright on the software, and (2) offer you this License | ensure that, in such cases, the modified source code becomes available | ||||||
| giving you legal permission to copy, distribute and/or modify it. | to the community.  It requires the operator of a network server to | ||||||
|  | provide the source code of the modified version running there to the | ||||||
|  | users of that server.  Therefore, public use of a modified version, on | ||||||
|  | a publicly accessible server, gives the public access to the source | ||||||
|  | code of the modified version. | ||||||
| 
 | 
 | ||||||
|   For the developers' and authors' protection, the GPL clearly explains |   An older license, called the Affero General Public License and | ||||||
| that there is no warranty for this free software.  For both users' and | published by Affero, was designed to accomplish similar goals.  This is | ||||||
| authors' sake, the GPL requires that modified versions be marked as | a different license, not a version of the Affero GPL, but Affero has | ||||||
| changed, so that their problems will not be attributed erroneously to | released a new version of the Affero GPL which permits relicensing under | ||||||
| authors of previous versions. | this license. | ||||||
| 
 |  | ||||||
|   Some devices are designed to deny users access to install or run |  | ||||||
| modified versions of the software inside them, although the manufacturer |  | ||||||
| can do so.  This is fundamentally incompatible with the aim of |  | ||||||
| protecting users' freedom to change the software.  The systematic |  | ||||||
| pattern of such abuse occurs in the area of products for individuals to |  | ||||||
| use, which is precisely where it is most unacceptable.  Therefore, we |  | ||||||
| have designed this version of the GPL to prohibit the practice for those |  | ||||||
| products.  If such problems arise substantially in other domains, we |  | ||||||
| stand ready to extend this provision to those domains in future versions |  | ||||||
| of the GPL, as needed to protect the freedom of users. |  | ||||||
| 
 |  | ||||||
|   Finally, every program is threatened constantly by software patents. |  | ||||||
| States should not allow patents to restrict development and use of |  | ||||||
| software on general-purpose computers, but in those that do, we wish to |  | ||||||
| avoid the special danger that patents applied to a free program could |  | ||||||
| make it effectively proprietary.  To prevent this, the GPL assures that |  | ||||||
| patents cannot be used to render the program non-free. |  | ||||||
| 
 | 
 | ||||||
|   The precise terms and conditions for copying, distribution and |   The precise terms and conditions for copying, distribution and | ||||||
| modification follow. | modification follow. | ||||||
|  | @ -72,7 +60,7 @@ modification follow. | ||||||
| 
 | 
 | ||||||
|   0. Definitions. |   0. Definitions. | ||||||
| 
 | 
 | ||||||
|   "This License" refers to version 3 of the GNU General Public License. |   "This License" refers to version 3 of the GNU Affero General Public License. | ||||||
| 
 | 
 | ||||||
|   "Copyright" also means copyright-like laws that apply to other kinds of |   "Copyright" also means copyright-like laws that apply to other kinds of | ||||||
| works, such as semiconductor masks. | works, such as semiconductor masks. | ||||||
|  | @ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey | ||||||
| the Program, the only way you could satisfy both those terms and this | the Program, the only way you could satisfy both those terms and this | ||||||
| License would be to refrain entirely from conveying the Program. | License would be to refrain entirely from conveying the Program. | ||||||
| 
 | 
 | ||||||
|   13. Use with the GNU Affero General Public License. |   13. Remote Network Interaction; Use with the GNU General Public License. | ||||||
|  | 
 | ||||||
|  |   Notwithstanding any other provision of this License, if you modify the | ||||||
|  | Program, your modified version must prominently offer all users | ||||||
|  | interacting with it remotely through a computer network (if your version | ||||||
|  | supports such interaction) an opportunity to receive the Corresponding | ||||||
|  | Source of your version by providing access to the Corresponding Source | ||||||
|  | from a network server at no charge, through some standard or customary | ||||||
|  | means of facilitating copying of software.  This Corresponding Source | ||||||
|  | shall include the Corresponding Source for any work covered by version 3 | ||||||
|  | of the GNU General Public License that is incorporated pursuant to the | ||||||
|  | following paragraph. | ||||||
| 
 | 
 | ||||||
|   Notwithstanding any other provision of this License, you have |   Notwithstanding any other provision of this License, you have | ||||||
| permission to link or combine any covered work with a work licensed | permission to link or combine any covered work with a work licensed | ||||||
| under version 3 of the GNU Affero General Public License into a single | under version 3 of the GNU General Public License into a single | ||||||
| combined work, and to convey the resulting work.  The terms of this | combined work, and to convey the resulting work.  The terms of this | ||||||
| License will continue to apply to the part which is the covered work, | License will continue to apply to the part which is the covered work, | ||||||
| but the special requirements of the GNU Affero General Public License, | but the work with which it is combined will remain governed by version | ||||||
| section 13, concerning interaction through a network will apply to the | 3 of the GNU General Public License. | ||||||
| combination as such. |  | ||||||
| 
 | 
 | ||||||
|   14. Revised Versions of this License. |   14. Revised Versions of this License. | ||||||
| 
 | 
 | ||||||
|   The Free Software Foundation may publish revised and/or new versions of |   The Free Software Foundation may publish revised and/or new versions of | ||||||
| the GNU General Public License from time to time.  Such new versions will | the GNU Affero General Public License from time to time.  Such new versions | ||||||
| be similar in spirit to the present version, but may differ in detail to | will be similar in spirit to the present version, but may differ in detail to | ||||||
| address new problems or concerns. | address new problems or concerns. | ||||||
| 
 | 
 | ||||||
|   Each version is given a distinguishing version number.  If the |   Each version is given a distinguishing version number.  If the | ||||||
| Program specifies that a certain numbered version of the GNU General | Program specifies that a certain numbered version of the GNU Affero General | ||||||
| Public License "or any later version" applies to it, you have the | Public License "or any later version" applies to it, you have the | ||||||
| option of following the terms and conditions either of that numbered | option of following the terms and conditions either of that numbered | ||||||
| version or of any later version published by the Free Software | version or of any later version published by the Free Software | ||||||
| Foundation.  If the Program does not specify a version number of the | Foundation.  If the Program does not specify a version number of the | ||||||
| GNU General Public License, you may choose any version ever published | GNU Affero General Public License, you may choose any version ever published | ||||||
| by the Free Software Foundation. | by the Free Software Foundation. | ||||||
| 
 | 
 | ||||||
|   If the Program specifies that a proxy can decide which future |   If the Program specifies that a proxy can decide which future | ||||||
| versions of the GNU General Public License can be used, that proxy's | versions of the GNU Affero General Public License can be used, that proxy's | ||||||
| public statement of acceptance of a version permanently authorizes you | public statement of acceptance of a version permanently authorizes you | ||||||
| to choose that version for the Program. | to choose that version for the Program. | ||||||
| 
 | 
 | ||||||
|  | @ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found. | ||||||
|     Copyright (C) <year>  <name of author> |     Copyright (C) <year>  <name of author> | ||||||
| 
 | 
 | ||||||
|     This program is free software: you can redistribute it and/or modify |     This program is free software: you can redistribute it and/or modify | ||||||
|     it under the terms of the GNU General Public License as published by |     it under the terms of the GNU Affero General Public License as published | ||||||
|     the Free Software Foundation, either version 3 of the License, or |     by the Free Software Foundation, either version 3 of the License, or | ||||||
|     (at your option) any later version. |     (at your option) any later version. | ||||||
| 
 | 
 | ||||||
|     This program is distributed in the hope that it will be useful, |     This program is distributed in the hope that it will be useful, | ||||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|     GNU General Public License for more details. |     GNU Affero General Public License for more details. | ||||||
| 
 | 
 | ||||||
|     You should have received a copy of the GNU General Public License |     You should have received a copy of the GNU Affero General Public License | ||||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>. |     along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| Also add information on how to contact you by electronic and paper mail. | Also add information on how to contact you by electronic and paper mail. | ||||||
| 
 | 
 | ||||||
|   If the program does terminal interaction, make it output a short |   If your software can interact with users remotely through a computer | ||||||
| notice like this when it starts in an interactive mode: | network, you should also make sure that it provides a way for users to | ||||||
| 
 | get its source.  For example, if your program is a web application, its | ||||||
|     <program>  Copyright (C) <year>  <name of author> | interface could display a "Source" link that leads users to an archive | ||||||
|     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | of the code.  There are many ways you could offer source, and different | ||||||
|     This is free software, and you are welcome to redistribute it | solutions will be better for different programs; see section 13 for the | ||||||
|     under certain conditions; type `show c' for details. | specific requirements. | ||||||
| 
 |  | ||||||
| The hypothetical commands `show w' and `show c' should show the appropriate |  | ||||||
| parts of the General Public License.  Of course, your program's commands |  | ||||||
| might be different; for a GUI interface, you would use an "about box". |  | ||||||
| 
 | 
 | ||||||
|   You should also get your employer (if you work as a programmer) or school, |   You should also get your employer (if you work as a programmer) or school, | ||||||
| if any, to sign a "copyright disclaimer" for the program, if necessary. | if any, to sign a "copyright disclaimer" for the program, if necessary. | ||||||
| For more information on this, and how to apply and follow the GNU GPL, see | For more information on this, and how to apply and follow the GNU AGPL, see | ||||||
| <https://www.gnu.org/licenses/>. | <https://www.gnu.org/licenses/>. | ||||||
| 
 |  | ||||||
|   The GNU General Public License does not permit incorporating your program |  | ||||||
| into proprietary programs.  If your program is a subroutine library, you |  | ||||||
| may consider it more useful to permit linking proprietary applications with |  | ||||||
| the library.  If this is what you want to do, use the GNU Lesser General |  | ||||||
| Public License instead of this License.  But first, please read |  | ||||||
| <https://www.gnu.org/licenses/why-not-lgpl.html>. |  | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| [](https://hub.docker.com/r/vaultwarden/server) | [](https://hub.docker.com/r/vaultwarden/server) | ||||||
| [](https://deps.rs/repo/github/dani-garcia/vaultwarden) | [](https://deps.rs/repo/github/dani-garcia/vaultwarden) | ||||||
| [](https://github.com/dani-garcia/vaultwarden/releases/latest) | [](https://github.com/dani-garcia/vaultwarden/releases/latest) | ||||||
| [](https://github.com/dani-garcia/vaultwarden/blob/main/LICENSE.txt) | [](https://github.com/dani-garcia/vaultwarden/blob/main/LICENSE.txt) | ||||||
| [](https://matrix.to/#/#vaultwarden:matrix.org) | [](https://matrix.to/#/#vaultwarden:matrix.org) | ||||||
| 
 | 
 | ||||||
| Image is based on [Rust implementation of Bitwarden API](https://github.com/dani-garcia/vaultwarden). | Image is based on [Rust implementation of Bitwarden API](https://github.com/dani-garcia/vaultwarden). | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ | ||||||
| {% else %} | {% else %} | ||||||
| {%   set package_arch_target_param = "" %} | {%   set package_arch_target_param = "" %} | ||||||
| {% endif %} | {% endif %} | ||||||
| {% if "buildx" in target_file %} | {% if "buildkit" in target_file %} | ||||||
| {%   set mount_rust_cache = "--mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry " %} | {%   set mount_rust_cache = "--mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry " %} | ||||||
| {% else %} | {% else %} | ||||||
| {%   set mount_rust_cache = "" %} | {%   set mount_rust_cache = "" %} | ||||||
|  | @ -83,8 +83,6 @@ FROM vaultwarden/web-vault@{{ vault_image_digest }} as vault | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM {{ build_stage_base_image }} as build | FROM {{ build_stage_base_image }} as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -93,7 +91,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN {{ mount_rust_cache -}} mkdir -pv "${CARGO_HOME}" \ | RUN {{ mount_rust_cache -}} mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -104,21 +101,20 @@ RUN {{ mount_rust_cache -}} mkdir -pv "${CARGO_HOME}" \ | ||||||
| ENV RUSTFLAGS='-Clink-arg=/usr/local/musl/{{ package_arch_target }}/lib/libatomic.a' | ENV RUSTFLAGS='-Clink-arg=/usr/local/musl/{{ package_arch_target }}/lib/libatomic.a' | ||||||
| {%   endif %} | {%   endif %} | ||||||
| {% elif "arm" in target_file %} | {% elif "arm" in target_file %} | ||||||
| # | # Install build dependencies for the {{ package_arch_name }} architecture | ||||||
| # Install required build libs for {{ package_arch_name }} architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture {{ package_arch_name }} \ | RUN dpkg --add-architecture {{ package_arch_name }} \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev{{ package_arch_prefix }} \ |         gcc-{{ package_cross_compiler }} \ | ||||||
|         libc6-dev{{ package_arch_prefix }} \ |         libc6-dev{{ package_arch_prefix }} \ | ||||||
|         libpq5{{ package_arch_prefix }} \ |         libcap2-bin \ | ||||||
|         libpq-dev{{ package_arch_prefix }} \ |  | ||||||
|         libmariadb3{{ package_arch_prefix }} \ |  | ||||||
|         libmariadb-dev{{ package_arch_prefix }} \ |         libmariadb-dev{{ package_arch_prefix }} \ | ||||||
|         libmariadb-dev-compat{{ package_arch_prefix }} \ |         libmariadb-dev-compat{{ package_arch_prefix }} \ | ||||||
|         gcc-{{ package_cross_compiler }} \ |         libmariadb3{{ package_arch_prefix }} \ | ||||||
|  |         libpq-dev{{ package_arch_prefix }} \ | ||||||
|  |         libpq5{{ package_arch_prefix }} \ | ||||||
|  |         libssl-dev{{ package_arch_prefix }} \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.{{ package_arch_target }}]' >> "${CARGO_HOME}/config" \ |     && echo '[target.{{ package_arch_target }}]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -130,16 +126,14 @@ ENV CC_{{ package_arch_target | replace("-", "_") }}="/usr/bin/{{ package_cross_ | ||||||
|     CROSS_COMPILE="1" \ |     CROSS_COMPILE="1" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/{{ package_cross_compiler }}" \ |     OPENSSL_INCLUDE_DIR="/usr/include/{{ package_cross_compiler }}" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/{{ package_cross_compiler }}" |     OPENSSL_LIB_DIR="/usr/lib/{{ package_cross_compiler }}" | ||||||
| 
 |  | ||||||
| {% elif "amd64" in target_file %} | {% elif "amd64" in target_file %} | ||||||
| # Install DB packages | # Install build dependencies | ||||||
| RUN apt-get update \ | RUN apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libmariadb-dev{{ package_arch_prefix }} \ |         libcap2-bin \ | ||||||
|         libpq-dev{{ package_arch_prefix }} \ |         libmariadb-dev \ | ||||||
|     && apt-get clean \ |         libpq-dev | ||||||
|     && rm -rf /var/lib/apt/lists/* |  | ||||||
| {% endif %} | {% endif %} | ||||||
| 
 | 
 | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
|  | @ -178,9 +172,20 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN {{ mount_rust_cache -}} cargo build --features ${DB} --release{{ package_arch_target_param }} | RUN {{ mount_rust_cache -}} cargo build --features ${DB} --release{{ package_arch_target_param }} | ||||||
| 
 | 
 | ||||||
|  | {% if "buildkit" in target_file %} | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | {%   if package_arch_target is defined %} | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/{{ package_arch_target }}/release/vaultwarden | ||||||
|  | {%   else %} | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/release/vaultwarden | ||||||
|  | {%   endif %} | ||||||
|  | {% endif %} | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -195,7 +200,6 @@ ENV ROCKET_PROFILE="release" \ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| {% if "amd64" not in target_file %} | {% if "amd64" not in target_file %} | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| {% endif %} | {% endif %} | ||||||
| 
 | 
 | ||||||
|  | @ -203,18 +207,18 @@ RUN [ "cross-build-start" ] | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
| {% if "alpine" in runtime_stage_base_image %} | {% if "alpine" in runtime_stage_base_image %} | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| {% else %} | {% else %} | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| {% endif %} | {% endif %} | ||||||
|  | @ -222,13 +226,11 @@ RUN mkdir /data \ | ||||||
| {% if "armv6" in target_file and "alpine" not in target_file %} | {% if "armv6" in target_file and "alpine" not in target_file %} | ||||||
| # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. | # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. | ||||||
| # This symlink was there in the buster images, and for some reason this is needed. | # This symlink was there in the buster images, and for some reason this is needed. | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 | RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 | ||||||
| 
 | 
 | ||||||
| {% endif -%} | {% endif -%} | ||||||
| 
 | 
 | ||||||
| {% if "amd64" not in target_file %} | {% if "amd64" not in target_file %} | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| {% endif %} | {% endif %} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,8 +8,8 @@ all: $(OBJECTS) | ||||||
| %/Dockerfile.alpine: Dockerfile.j2 render_template | %/Dockerfile.alpine: Dockerfile.j2 render_template | ||||||
| 	./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" | 	./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" | ||||||
| 
 | 
 | ||||||
| %/Dockerfile.buildx: Dockerfile.j2 render_template | %/Dockerfile.buildkit: Dockerfile.j2 render_template | ||||||
| 	./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" | 	./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" | ||||||
| 
 | 
 | ||||||
| %/Dockerfile.buildx.alpine: Dockerfile.j2 render_template | %/Dockerfile.buildkit.alpine: Dockerfile.j2 render_template | ||||||
| 	./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" | 	./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,19 +37,17 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # Install DB packages | # Install build dependencies | ||||||
| RUN apt-get update \ | RUN apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|  |         libcap2-bin \ | ||||||
|         libmariadb-dev \ |         libmariadb-dev \ | ||||||
|         libpq-dev \ |         libpq-dev | ||||||
|     && apt-get clean \ |  | ||||||
|     && rm -rf /var/lib/apt/lists/* |  | ||||||
| 
 | 
 | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
|  | @ -81,9 +77,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release | RUN cargo build --features ${DB} --release | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -98,11 +94,11 @@ ENV ROCKET_PROFILE="release" \ | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:x86_64-musl-stable-1.66.1 as build | FROM blackdex/rust-musl:x86_64-musl-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -75,9 +72,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl | RUN cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -93,10 +90,10 @@ ENV ROCKET_PROFILE="release" \ | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,19 +37,17 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # Install DB packages | # Install build dependencies | ||||||
| RUN apt-get update \ | RUN apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|  |         libcap2-bin \ | ||||||
|         libmariadb-dev \ |         libmariadb-dev \ | ||||||
|         libpq-dev \ |         libpq-dev | ||||||
|     && apt-get clean \ |  | ||||||
|     && rm -rf /var/lib/apt/lists/* |  | ||||||
| 
 | 
 | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
|  | @ -81,9 +77,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -98,11 +99,11 @@ ENV ROCKET_PROFILE="release" \ | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:x86_64-musl-stable-1.66.1 as build | FROM blackdex/rust-musl:x86_64-musl-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -75,9 +72,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/x86_64-unknown-linux-musl/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -93,10 +95,10 @@ ENV ROCKET_PROFILE="release" \ | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,26 +37,24 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # | # Install build dependencies for the arm64 architecture | ||||||
| # Install required build libs for arm64 architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture arm64 \ | RUN dpkg --add-architecture arm64 \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev:arm64 \ |         gcc-aarch64-linux-gnu \ | ||||||
|         libc6-dev:arm64 \ |         libc6-dev:arm64 \ | ||||||
|         libpq5:arm64 \ |         libcap2-bin \ | ||||||
|         libpq-dev:arm64 \ |  | ||||||
|         libmariadb3:arm64 \ |  | ||||||
|         libmariadb-dev:arm64 \ |         libmariadb-dev:arm64 \ | ||||||
|         libmariadb-dev-compat:arm64 \ |         libmariadb-dev-compat:arm64 \ | ||||||
|         gcc-aarch64-linux-gnu \ |         libmariadb3:arm64 \ | ||||||
|  |         libpq-dev:arm64 \ | ||||||
|  |         libpq5:arm64 \ | ||||||
|  |         libssl-dev:arm64 \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.aarch64-unknown-linux-gnu]' >> "${CARGO_HOME}/config" \ |     && echo '[target.aarch64-unknown-linux-gnu]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -71,7 +67,6 @@ ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu" \ |     OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu" |     OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | @ -101,9 +96,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu | RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -113,22 +108,20 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     ROCKET_ADDRESS=0.0.0.0 \ |     ROCKET_ADDRESS=0.0.0.0 \ | ||||||
|     ROCKET_PORT=80 |     ROCKET_PORT=80 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:aarch64-musl-stable-1.66.1 as build | FROM blackdex/rust-musl:aarch64-musl-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -75,9 +72,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl | RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -89,18 +86,16 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     SSL_CERT_DIR=/etc/ssl/certs |     SSL_CERT_DIR=/etc/ssl/certs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,26 +37,24 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # | # Install build dependencies for the arm64 architecture | ||||||
| # Install required build libs for arm64 architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture arm64 \ | RUN dpkg --add-architecture arm64 \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev:arm64 \ |         gcc-aarch64-linux-gnu \ | ||||||
|         libc6-dev:arm64 \ |         libc6-dev:arm64 \ | ||||||
|         libpq5:arm64 \ |         libcap2-bin \ | ||||||
|         libpq-dev:arm64 \ |  | ||||||
|         libmariadb3:arm64 \ |  | ||||||
|         libmariadb-dev:arm64 \ |         libmariadb-dev:arm64 \ | ||||||
|         libmariadb-dev-compat:arm64 \ |         libmariadb-dev-compat:arm64 \ | ||||||
|         gcc-aarch64-linux-gnu \ |         libmariadb3:arm64 \ | ||||||
|  |         libpq-dev:arm64 \ | ||||||
|  |         libpq5:arm64 \ | ||||||
|  |         libssl-dev:arm64 \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.aarch64-unknown-linux-gnu]' >> "${CARGO_HOME}/config" \ |     && echo '[target.aarch64-unknown-linux-gnu]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -71,7 +67,6 @@ ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu" \ |     OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu" |     OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | @ -101,9 +96,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/aarch64-unknown-linux-gnu/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -113,22 +113,20 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     ROCKET_ADDRESS=0.0.0.0 \ |     ROCKET_ADDRESS=0.0.0.0 \ | ||||||
|     ROCKET_PORT=80 |     ROCKET_PORT=80 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:aarch64-musl-stable-1.66.1 as build | FROM blackdex/rust-musl:aarch64-musl-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -75,9 +72,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/aarch64-unknown-linux-musl/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -89,18 +91,16 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     SSL_CERT_DIR=/etc/ssl/certs |     SSL_CERT_DIR=/etc/ssl/certs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,26 +37,24 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # | # Install build dependencies for the armel architecture | ||||||
| # Install required build libs for armel architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture armel \ | RUN dpkg --add-architecture armel \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev:armel \ |         gcc-arm-linux-gnueabi \ | ||||||
|         libc6-dev:armel \ |         libc6-dev:armel \ | ||||||
|         libpq5:armel \ |         libcap2-bin \ | ||||||
|         libpq-dev:armel \ |  | ||||||
|         libmariadb3:armel \ |  | ||||||
|         libmariadb-dev:armel \ |         libmariadb-dev:armel \ | ||||||
|         libmariadb-dev-compat:armel \ |         libmariadb-dev-compat:armel \ | ||||||
|         gcc-arm-linux-gnueabi \ |         libmariadb3:armel \ | ||||||
|  |         libpq-dev:armel \ | ||||||
|  |         libpq5:armel \ | ||||||
|  |         libssl-dev:armel \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.arm-unknown-linux-gnueabi]' >> "${CARGO_HOME}/config" \ |     && echo '[target.arm-unknown-linux-gnueabi]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -71,7 +67,6 @@ ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi" \ |     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi" |     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | @ -101,9 +96,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi | RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -113,27 +108,24 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     ROCKET_ADDRESS=0.0.0.0 \ |     ROCKET_ADDRESS=0.0.0.0 \ | ||||||
|     ROCKET_PORT=80 |     ROCKET_PORT=80 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. | # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. | ||||||
| # This symlink was there in the buster images, and for some reason this is needed. | # This symlink was there in the buster images, and for some reason this is needed. | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 | RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:arm-musleabi-stable-1.66.1 as build | FROM blackdex/rust-musl:arm-musleabi-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -77,9 +74,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi | RUN cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -91,18 +88,16 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     SSL_CERT_DIR=/etc/ssl/certs |     SSL_CERT_DIR=/etc/ssl/certs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,26 +37,24 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # | # Install build dependencies for the armel architecture | ||||||
| # Install required build libs for armel architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture armel \ | RUN dpkg --add-architecture armel \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev:armel \ |         gcc-arm-linux-gnueabi \ | ||||||
|         libc6-dev:armel \ |         libc6-dev:armel \ | ||||||
|         libpq5:armel \ |         libcap2-bin \ | ||||||
|         libpq-dev:armel \ |  | ||||||
|         libmariadb3:armel \ |  | ||||||
|         libmariadb-dev:armel \ |         libmariadb-dev:armel \ | ||||||
|         libmariadb-dev-compat:armel \ |         libmariadb-dev-compat:armel \ | ||||||
|         gcc-arm-linux-gnueabi \ |         libmariadb3:armel \ | ||||||
|  |         libpq-dev:armel \ | ||||||
|  |         libpq5:armel \ | ||||||
|  |         libssl-dev:armel \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.arm-unknown-linux-gnueabi]' >> "${CARGO_HOME}/config" \ |     && echo '[target.arm-unknown-linux-gnueabi]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -71,7 +67,6 @@ ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi" \ |     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi" |     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | @ -101,9 +96,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/arm-unknown-linux-gnueabi/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -113,27 +113,24 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     ROCKET_ADDRESS=0.0.0.0 \ |     ROCKET_ADDRESS=0.0.0.0 \ | ||||||
|     ROCKET_PORT=80 |     ROCKET_PORT=80 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. | # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. | ||||||
| # This symlink was there in the buster images, and for some reason this is needed. | # This symlink was there in the buster images, and for some reason this is needed. | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 | RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:arm-musleabi-stable-1.66.1 as build | FROM blackdex/rust-musl:arm-musleabi-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -77,9 +74,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/arm-unknown-linux-musleabi/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -91,18 +93,16 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     SSL_CERT_DIR=/etc/ssl/certs |     SSL_CERT_DIR=/etc/ssl/certs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,26 +37,24 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # | # Install build dependencies for the armhf architecture | ||||||
| # Install required build libs for armhf architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture armhf \ | RUN dpkg --add-architecture armhf \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev:armhf \ |         gcc-arm-linux-gnueabihf \ | ||||||
|         libc6-dev:armhf \ |         libc6-dev:armhf \ | ||||||
|         libpq5:armhf \ |         libcap2-bin \ | ||||||
|         libpq-dev:armhf \ |  | ||||||
|         libmariadb3:armhf \ |  | ||||||
|         libmariadb-dev:armhf \ |         libmariadb-dev:armhf \ | ||||||
|         libmariadb-dev-compat:armhf \ |         libmariadb-dev-compat:armhf \ | ||||||
|         gcc-arm-linux-gnueabihf \ |         libmariadb3:armhf \ | ||||||
|  |         libpq-dev:armhf \ | ||||||
|  |         libpq5:armhf \ | ||||||
|  |         libssl-dev:armhf \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.armv7-unknown-linux-gnueabihf]' >> "${CARGO_HOME}/config" \ |     && echo '[target.armv7-unknown-linux-gnueabihf]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -71,7 +67,6 @@ ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf" \ |     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf" |     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | @ -101,9 +96,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf | RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -113,22 +108,20 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     ROCKET_ADDRESS=0.0.0.0 \ |     ROCKET_ADDRESS=0.0.0.0 \ | ||||||
|     ROCKET_PORT=80 |     ROCKET_PORT=80 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.1 as build | FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN mkdir -pv "${CARGO_HOME}" \ | RUN mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -75,9 +72,9 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf | RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -89,18 +86,16 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     SSL_CERT_DIR=/etc/ssl/certs |     SSL_CERT_DIR=/etc/ssl/certs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  |  | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM rust:1.66-bullseye as build | FROM rust:1.66-bullseye as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,26 +37,24 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
| 
 | 
 | ||||||
| # | # Install build dependencies for the armhf architecture | ||||||
| # Install required build libs for armhf architecture. |  | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN dpkg --add-architecture armhf \ | RUN dpkg --add-architecture armhf \ | ||||||
|     && apt-get update \ |     && apt-get update \ | ||||||
|     && apt-get install -y \ |     && apt-get install -y \ | ||||||
|         --no-install-recommends \ |         --no-install-recommends \ | ||||||
|         libssl-dev:armhf \ |         gcc-arm-linux-gnueabihf \ | ||||||
|         libc6-dev:armhf \ |         libc6-dev:armhf \ | ||||||
|         libpq5:armhf \ |         libcap2-bin \ | ||||||
|         libpq-dev:armhf \ |  | ||||||
|         libmariadb3:armhf \ |  | ||||||
|         libmariadb-dev:armhf \ |         libmariadb-dev:armhf \ | ||||||
|         libmariadb-dev-compat:armhf \ |         libmariadb-dev-compat:armhf \ | ||||||
|         gcc-arm-linux-gnueabihf \ |         libmariadb3:armhf \ | ||||||
|  |         libpq-dev:armhf \ | ||||||
|  |         libpq5:armhf \ | ||||||
|  |         libssl-dev:armhf \ | ||||||
|     # |     # | ||||||
|     # Make sure cargo has the right target config |     # Make sure cargo has the right target config | ||||||
|     && echo '[target.armv7-unknown-linux-gnueabihf]' >> "${CARGO_HOME}/config" \ |     && echo '[target.armv7-unknown-linux-gnueabihf]' >> "${CARGO_HOME}/config" \ | ||||||
|  | @ -71,7 +67,6 @@ ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc" \ | ||||||
|     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf" \ |     OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf" \ | ||||||
|     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf" |     OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Creates a dummy project used to grab dependencies | # Creates a dummy project used to grab dependencies | ||||||
| RUN USER=root cargo new --bin /app | RUN USER=root cargo new --bin /app | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  | @ -101,9 +96,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/armv7-unknown-linux-gnueabihf/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -113,22 +113,20 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     ROCKET_ADDRESS=0.0.0.0 \ |     ROCKET_ADDRESS=0.0.0.0 \ | ||||||
|     ROCKET_PORT=80 |     ROCKET_PORT=80 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apt-get update && apt-get install -y \ |     && apt-get update && apt-get install -y \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     openssl \ |  | ||||||
|     ca-certificates \ |     ca-certificates \ | ||||||
|     curl \ |     curl \ | ||||||
|     libmariadb-dev-compat \ |     libmariadb-dev-compat \ | ||||||
|     libpq5 \ |     libpq5 \ | ||||||
|  |     openssl \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -29,8 +29,6 @@ FROM vaultwarden/web-vault@sha256:d5f71fb05c4b87935bf51d84140db0f8716cabfe2974fb | ||||||
| ########################## BUILD IMAGE  ########################## | ########################## BUILD IMAGE  ########################## | ||||||
| FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.1 as build | FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.1 as build | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Build time options to avoid dpkg warnings and help with reproducible builds. | # Build time options to avoid dpkg warnings and help with reproducible builds. | ||||||
| ENV DEBIAN_FRONTEND=noninteractive \ | ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     LANG=C.UTF-8 \ |     LANG=C.UTF-8 \ | ||||||
|  | @ -39,7 +37,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ | ||||||
|     CARGO_HOME="/root/.cargo" \ |     CARGO_HOME="/root/.cargo" \ | ||||||
|     USER="root" |     USER="root" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Create CARGO_HOME folder and don't download rust docs | # Create CARGO_HOME folder and don't download rust docs | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ | ||||||
|     && rustup set profile minimal |     && rustup set profile minimal | ||||||
|  | @ -75,9 +72,14 @@ RUN touch src/main.rs | ||||||
| 
 | 
 | ||||||
| # Builds again, this time it'll just be | # Builds again, this time it'll just be | ||||||
| # your actual source files being built | # your actual source files being built | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf | RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf | ||||||
| 
 | 
 | ||||||
|  | # Add the `cap_net_bind_service` capability to allow listening on | ||||||
|  | # privileged (< 1024) ports even when running as a non-root user. | ||||||
|  | # This is only done if building with BuildKit; with the legacy | ||||||
|  | # builder, the `COPY` instruction doesn't carry over capabilities. | ||||||
|  | RUN setcap cap_net_bind_service=+ep target/armv7-unknown-linux-musleabihf/release/vaultwarden | ||||||
|  | 
 | ||||||
| ######################## RUNTIME IMAGE  ######################## | ######################## RUNTIME IMAGE  ######################## | ||||||
| # Create a new stage with a minimal image | # Create a new stage with a minimal image | ||||||
| # because we already have a binary built | # because we already have a binary built | ||||||
|  | @ -89,18 +91,16 @@ ENV ROCKET_PROFILE="release" \ | ||||||
|     SSL_CERT_DIR=/etc/ssl/certs |     SSL_CERT_DIR=/etc/ssl/certs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-start" ] | RUN [ "cross-build-start" ] | ||||||
| 
 | 
 | ||||||
| # Create data folder and Install needed libraries | # Create data folder and Install needed libraries | ||||||
| RUN mkdir /data \ | RUN mkdir /data \ | ||||||
|     && apk add --no-cache \ |     && apk add --no-cache \ | ||||||
|         openssl \ |         ca-certificates \ | ||||||
|         tzdata \ |  | ||||||
|         curl \ |         curl \ | ||||||
|         ca-certificates |         openssl \ | ||||||
|  |         tzdata | ||||||
| 
 | 
 | ||||||
| # hadolint ignore=DL3059 |  | ||||||
| RUN [ "cross-build-end" ] | RUN [ "cross-build-end" ] | ||||||
| 
 | 
 | ||||||
| VOLUME /data | VOLUME /data | ||||||
|  | @ -23,7 +23,7 @@ LABELS=( | ||||||
|     # https://github.com/opencontainers/image-spec/blob/master/annotations.md |     # https://github.com/opencontainers/image-spec/blob/master/annotations.md | ||||||
|     org.opencontainers.image.created="$(date --utc --iso-8601=seconds)" |     org.opencontainers.image.created="$(date --utc --iso-8601=seconds)" | ||||||
|     org.opencontainers.image.documentation="https://github.com/dani-garcia/vaultwarden/wiki" |     org.opencontainers.image.documentation="https://github.com/dani-garcia/vaultwarden/wiki" | ||||||
|     org.opencontainers.image.licenses="GPL-3.0-only" |     org.opencontainers.image.licenses="AGPL-3.0-only" | ||||||
|     org.opencontainers.image.revision="${SOURCE_COMMIT}" |     org.opencontainers.image.revision="${SOURCE_COMMIT}" | ||||||
|     org.opencontainers.image.source="${SOURCE_REPOSITORY_URL}" |     org.opencontainers.image.source="${SOURCE_REPOSITORY_URL}" | ||||||
|     org.opencontainers.image.url="https://hub.docker.com/r/${DOCKER_REPO#*/}" |     org.opencontainers.image.url="https://hub.docker.com/r/${DOCKER_REPO#*/}" | ||||||
|  | @ -34,9 +34,9 @@ for label in "${LABELS[@]}"; do | ||||||
|     LABEL_ARGS+=(--label "${label}") |     LABEL_ARGS+=(--label "${label}") | ||||||
| done | done | ||||||
| 
 | 
 | ||||||
| # Check if DOCKER_BUILDKIT is set, if so, use the Dockerfile.buildx as template | # Check if DOCKER_BUILDKIT is set, if so, use the Dockerfile.buildkit as template | ||||||
| if [[ -n "${DOCKER_BUILDKIT}" ]]; then | if [[ -n "${DOCKER_BUILDKIT}" ]]; then | ||||||
|     buildx_suffix=.buildx |     buildkit_suffix=.buildkit | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| set -ex | set -ex | ||||||
|  | @ -45,6 +45,6 @@ for arch in "${arches[@]}"; do | ||||||
|     docker build \ |     docker build \ | ||||||
|            "${LABEL_ARGS[@]}" \ |            "${LABEL_ARGS[@]}" \ | ||||||
|            -t "${DOCKER_REPO}:${DOCKER_TAG}-${arch}" \ |            -t "${DOCKER_REPO}:${DOCKER_TAG}-${arch}" \ | ||||||
|            -f docker/${arch}/Dockerfile${buildx_suffix}${distro_suffix} \ |            -f docker/${arch}/Dockerfile${buildkit_suffix}${distro_suffix} \ | ||||||
|            . |            . | ||||||
| done | done | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | ALTER TABLE users_organizations | ||||||
|  | ADD COLUMN reset_password_key TEXT; | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | ALTER TABLE users_organizations | ||||||
|  | ADD COLUMN reset_password_key TEXT; | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | ALTER TABLE users_organizations | ||||||
|  | ADD COLUMN reset_password_key TEXT; | ||||||
|  | @ -123,7 +123,9 @@ async fn post_emergency_access( | ||||||
| 
 | 
 | ||||||
|     emergency_access.atype = new_type; |     emergency_access.atype = new_type; | ||||||
|     emergency_access.wait_time_days = data.WaitTimeDays; |     emergency_access.wait_time_days = data.WaitTimeDays; | ||||||
|  |     if data.KeyEncrypted.is_some() { | ||||||
|         emergency_access.key_encrypted = data.KeyEncrypted; |         emergency_access.key_encrypted = data.KeyEncrypted; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     emergency_access.save(&mut conn).await?; |     emergency_access.save(&mut conn).await?; | ||||||
|     Ok(Json(emergency_access.to_json())) |     Ok(Json(emergency_access.to_json())) | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ pub fn routes() -> Vec<Route> { | ||||||
|         get_plans_tax_rates, |         get_plans_tax_rates, | ||||||
|         import, |         import, | ||||||
|         post_org_keys, |         post_org_keys, | ||||||
|  |         get_organization_keys, | ||||||
|         bulk_public_keys, |         bulk_public_keys, | ||||||
|         deactivate_organization_user, |         deactivate_organization_user, | ||||||
|         bulk_deactivate_organization_user, |         bulk_deactivate_organization_user, | ||||||
|  | @ -86,6 +87,9 @@ pub fn routes() -> Vec<Route> { | ||||||
|         put_user_groups, |         put_user_groups, | ||||||
|         delete_group_user, |         delete_group_user, | ||||||
|         post_delete_group_user, |         post_delete_group_user, | ||||||
|  |         put_reset_password_enrollment, | ||||||
|  |         get_reset_password_details, | ||||||
|  |         put_reset_password, | ||||||
|         get_org_export |         get_org_export | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|  | @ -882,6 +886,7 @@ async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, co | ||||||
| #[allow(non_snake_case)] | #[allow(non_snake_case)] | ||||||
| struct AcceptData { | struct AcceptData { | ||||||
|     Token: String, |     Token: String, | ||||||
|  |     ResetPasswordKey: Option<String>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[post("/organizations/<org_id>/users/<_org_user_id>/accept", data = "<data>")] | #[post("/organizations/<org_id>/users/<_org_user_id>/accept", data = "<data>")] | ||||||
|  | @ -909,6 +914,11 @@ async fn accept_invite( | ||||||
|                     err!("User already accepted the invitation") |                     err!("User already accepted the invitation") | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |                 let master_password_required = OrgPolicy::org_is_reset_password_auto_enroll(org, &mut conn).await; | ||||||
|  |                 if data.ResetPasswordKey.is_none() && master_password_required { | ||||||
|  |                     err!("Reset password key is required, but not provided."); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type
 |                 // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type
 | ||||||
|                 // It returns different error messages per function.
 |                 // It returns different error messages per function.
 | ||||||
|                 if user_org.atype < UserOrgType::Admin { |                 if user_org.atype < UserOrgType::Admin { | ||||||
|  | @ -924,6 +934,11 @@ async fn accept_invite( | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 user_org.status = UserOrgStatus::Accepted as i32; |                 user_org.status = UserOrgStatus::Accepted as i32; | ||||||
|  | 
 | ||||||
|  |                 if master_password_required { | ||||||
|  |                     user_org.reset_password_key = data.ResetPasswordKey; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 user_org.save(&mut conn).await?; |                 user_org.save(&mut conn).await?; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -2460,6 +2475,204 @@ async fn delete_group_user( | ||||||
|     GroupUser::delete_by_group_id_and_user_id(&group_id, &org_user_id, &mut conn).await |     GroupUser::delete_by_group_id_and_user_id(&group_id, &org_user_id, &mut conn).await | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[derive(Deserialize)] | ||||||
|  | #[allow(non_snake_case)] | ||||||
|  | struct OrganizationUserResetPasswordEnrollmentRequest { | ||||||
|  |     ResetPasswordKey: Option<String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Deserialize)] | ||||||
|  | #[allow(non_snake_case)] | ||||||
|  | struct OrganizationUserResetPasswordRequest { | ||||||
|  |     NewMasterPasswordHash: String, | ||||||
|  |     Key: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[get("/organizations/<org_id>/keys")] | ||||||
|  | async fn get_organization_keys(org_id: String, mut conn: DbConn) -> JsonResult { | ||||||
|  |     let org = match Organization::find_by_uuid(&org_id, &mut conn).await { | ||||||
|  |         Some(organization) => organization, | ||||||
|  |         None => err!("Organization not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Ok(Json(json!({ | ||||||
|  |         "Object": "organizationKeys", | ||||||
|  |         "PublicKey": org.public_key, | ||||||
|  |         "PrivateKey": org.private_key, | ||||||
|  |     }))) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[put("/organizations/<org_id>/users/<org_user_id>/reset-password", data = "<data>")] | ||||||
|  | async fn put_reset_password( | ||||||
|  |     org_id: String, | ||||||
|  |     org_user_id: String, | ||||||
|  |     headers: AdminHeaders, | ||||||
|  |     data: JsonUpcase<OrganizationUserResetPasswordRequest>, | ||||||
|  |     mut conn: DbConn, | ||||||
|  |     ip: ClientIp, | ||||||
|  |     nt: Notify<'_>, | ||||||
|  | ) -> EmptyResult { | ||||||
|  |     let org = match Organization::find_by_uuid(&org_id, &mut conn).await { | ||||||
|  |         Some(org) => org, | ||||||
|  |         None => err!("Required organization not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let org_user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org.uuid, &mut conn).await { | ||||||
|  |         Some(user) => user, | ||||||
|  |         None => err!("User to reset isn't member of required organization"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let mut user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { | ||||||
|  |         Some(user) => user, | ||||||
|  |         None => err!("User not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     check_reset_password_applicable_and_permissions(&org_id, &org_user_id, &headers, &mut conn).await?; | ||||||
|  | 
 | ||||||
|  |     if org_user.reset_password_key.is_none() { | ||||||
|  |         err!("Password reset not or not correctly enrolled"); | ||||||
|  |     } | ||||||
|  |     if org_user.status != (UserOrgStatus::Confirmed as i32) { | ||||||
|  |         err!("Organization user must be confirmed for password reset functionality"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Sending email before resetting password to ensure working email configuration and the resulting
 | ||||||
|  |     // user notification. Also this might add some protection against security flaws and misuse
 | ||||||
|  |     if let Err(e) = mail::send_admin_reset_password(&user.email, &user.name, &org.name).await { | ||||||
|  |         error!("Error sending user reset password email: {:#?}", e); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let reset_request = data.into_inner().data; | ||||||
|  | 
 | ||||||
|  |     user.set_password(reset_request.NewMasterPasswordHash.as_str(), Some(reset_request.Key), true, None); | ||||||
|  |     user.save(&mut conn).await?; | ||||||
|  | 
 | ||||||
|  |     nt.send_logout(&user, None).await; | ||||||
|  | 
 | ||||||
|  |     log_event( | ||||||
|  |         EventType::OrganizationUserAdminResetPassword as i32, | ||||||
|  |         &org_user_id, | ||||||
|  |         org.uuid.clone(), | ||||||
|  |         headers.user.uuid.clone(), | ||||||
|  |         headers.device.atype, | ||||||
|  |         &ip.ip, | ||||||
|  |         &mut conn, | ||||||
|  |     ) | ||||||
|  |     .await; | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[get("/organizations/<org_id>/users/<org_user_id>/reset-password-details")] | ||||||
|  | async fn get_reset_password_details( | ||||||
|  |     org_id: String, | ||||||
|  |     org_user_id: String, | ||||||
|  |     headers: AdminHeaders, | ||||||
|  |     mut conn: DbConn, | ||||||
|  | ) -> JsonResult { | ||||||
|  |     let org = match Organization::find_by_uuid(&org_id, &mut conn).await { | ||||||
|  |         Some(org) => org, | ||||||
|  |         None => err!("Required organization not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let org_user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &mut conn).await { | ||||||
|  |         Some(user) => user, | ||||||
|  |         None => err!("User to reset isn't member of required organization"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { | ||||||
|  |         Some(user) => user, | ||||||
|  |         None => err!("User not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     check_reset_password_applicable_and_permissions(&org_id, &org_user_id, &headers, &mut conn).await?; | ||||||
|  | 
 | ||||||
|  |     Ok(Json(json!({ | ||||||
|  |         "Object": "organizationUserResetPasswordDetails", | ||||||
|  |         "Kdf":user.client_kdf_type, | ||||||
|  |         "KdfIterations":user.client_kdf_iter, | ||||||
|  |         "ResetPasswordKey":org_user.reset_password_key, | ||||||
|  |         "EncryptedPrivateKey":org.private_key , | ||||||
|  | 
 | ||||||
|  |     }))) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async fn check_reset_password_applicable_and_permissions( | ||||||
|  |     org_id: &str, | ||||||
|  |     org_user_id: &str, | ||||||
|  |     headers: &AdminHeaders, | ||||||
|  |     conn: &mut DbConn, | ||||||
|  | ) -> EmptyResult { | ||||||
|  |     check_reset_password_applicable(org_id, conn).await?; | ||||||
|  | 
 | ||||||
|  |     let target_user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { | ||||||
|  |         Some(user) => user, | ||||||
|  |         None => err!("Reset target user not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Resetting user must be higher/equal to user to reset
 | ||||||
|  |     match headers.org_user_type { | ||||||
|  |         UserOrgType::Owner => Ok(()), | ||||||
|  |         UserOrgType::Admin if target_user.atype <= UserOrgType::Admin => Ok(()), | ||||||
|  |         _ => err!("No permission to reset this user's password"), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async fn check_reset_password_applicable(org_id: &str, conn: &mut DbConn) -> EmptyResult { | ||||||
|  |     if !CONFIG.mail_enabled() { | ||||||
|  |         err!("Password reset is not supported on an email-disabled instance."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let policy = match OrgPolicy::find_by_org_and_type(org_id, OrgPolicyType::ResetPassword, conn).await { | ||||||
|  |         Some(p) => p, | ||||||
|  |         None => err!("Policy not found"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if !policy.enabled { | ||||||
|  |         err!("Reset password policy not enabled"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[put("/organizations/<org_id>/users/<org_user_id>/reset-password-enrollment", data = "<data>")] | ||||||
|  | async fn put_reset_password_enrollment( | ||||||
|  |     org_id: String, | ||||||
|  |     org_user_id: String, | ||||||
|  |     headers: Headers, | ||||||
|  |     data: JsonUpcase<OrganizationUserResetPasswordEnrollmentRequest>, | ||||||
|  |     mut conn: DbConn, | ||||||
|  |     ip: ClientIp, | ||||||
|  | ) -> EmptyResult { | ||||||
|  |     let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { | ||||||
|  |         Some(u) => u, | ||||||
|  |         None => err!("User to enroll isn't member of required organization"), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     check_reset_password_applicable(&org_id, &mut conn).await?; | ||||||
|  | 
 | ||||||
|  |     let reset_request = data.into_inner().data; | ||||||
|  | 
 | ||||||
|  |     if reset_request.ResetPasswordKey.is_none() | ||||||
|  |         && OrgPolicy::org_is_reset_password_auto_enroll(&org_id, &mut conn).await | ||||||
|  |     { | ||||||
|  |         err!("Reset password can't be withdrawed due to an enterprise policy"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     org_user.reset_password_key = reset_request.ResetPasswordKey; | ||||||
|  |     org_user.save(&mut conn).await?; | ||||||
|  | 
 | ||||||
|  |     let log_id = if org_user.reset_password_key.is_some() { | ||||||
|  |         EventType::OrganizationUserResetPasswordEnroll as i32 | ||||||
|  |     } else { | ||||||
|  |         EventType::OrganizationUserResetPasswordWithdraw as i32 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     log_event(log_id, &org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &ip.ip, &mut conn).await; | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // This is a new function active since the v2022.9.x clients.
 | // This is a new function active since the v2022.9.x clients.
 | ||||||
| // It combines the previous two calls done before.
 | // It combines the previous two calls done before.
 | ||||||
| // We call those two functions here and combine them our selfs.
 | // We call those two functions here and combine them our selfs.
 | ||||||
|  |  | ||||||
|  | @ -79,7 +79,7 @@ async fn icon_redirect(domain: &str, template: &str) -> Option<Redirect> { | ||||||
|         return None; |         return None; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if is_domain_blacklisted(domain).await { |     if check_domain_blacklist_reason(domain).await.is_some() { | ||||||
|         return None; |         return None; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -258,9 +258,15 @@ mod tests { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | enum DomainBlacklistReason { | ||||||
|  |     Regex, | ||||||
|  |     IP, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| use cached::proc_macro::cached; | use cached::proc_macro::cached; | ||||||
| #[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60)] | #[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60)] | ||||||
| async fn is_domain_blacklisted(domain: &str) -> bool { | async fn check_domain_blacklist_reason(domain: &str) -> Option<DomainBlacklistReason> { | ||||||
|     // First check the blacklist regex if there is a match.
 |     // First check the blacklist regex if there is a match.
 | ||||||
|     // This prevents the blocked domain(s) from being leaked via a DNS lookup.
 |     // This prevents the blocked domain(s) from being leaked via a DNS lookup.
 | ||||||
|     if let Some(blacklist) = CONFIG.icon_blacklist_regex() { |     if let Some(blacklist) = CONFIG.icon_blacklist_regex() { | ||||||
|  | @ -284,7 +290,7 @@ async fn is_domain_blacklisted(domain: &str) -> bool { | ||||||
| 
 | 
 | ||||||
|         if is_match { |         if is_match { | ||||||
|             debug!("Blacklisted domain: {} matched ICON_BLACKLIST_REGEX", domain); |             debug!("Blacklisted domain: {} matched ICON_BLACKLIST_REGEX", domain); | ||||||
|             return true; |             return Some(DomainBlacklistReason::Regex); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -293,13 +299,13 @@ async fn is_domain_blacklisted(domain: &str) -> bool { | ||||||
|             for addr in s { |             for addr in s { | ||||||
|                 if !is_global(addr.ip()) { |                 if !is_global(addr.ip()) { | ||||||
|                     debug!("IP {} for domain '{}' is not a global IP!", addr.ip(), domain); |                     debug!("IP {} for domain '{}' is not a global IP!", addr.ip(), domain); | ||||||
|                     return true; |                     return Some(DomainBlacklistReason::IP); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     false |     None | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn get_icon(domain: &str) -> Option<(Vec<u8>, String)> { | async fn get_icon(domain: &str) -> Option<(Vec<u8>, String)> { | ||||||
|  | @ -564,8 +570,10 @@ async fn get_page(url: &str) -> Result<Response, Error> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn get_page_with_referer(url: &str, referer: &str) -> Result<Response, Error> { | async fn get_page_with_referer(url: &str, referer: &str) -> Result<Response, Error> { | ||||||
|     if is_domain_blacklisted(url::Url::parse(url).unwrap().host_str().unwrap_or_default()).await { |     match check_domain_blacklist_reason(url::Url::parse(url).unwrap().host_str().unwrap_or_default()).await { | ||||||
|         warn!("Favicon '{}' resolves to a blacklisted domain or IP!", url); |         Some(DomainBlacklistReason::Regex) => warn!("Favicon '{}' is from a blacklisted domain!", url), | ||||||
|  |         Some(DomainBlacklistReason::IP) => warn!("Favicon '{}' is hosted on a non-global IP!", url), | ||||||
|  |         None => (), | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let mut client = CLIENT.get(url); |     let mut client = CLIENT.get(url); | ||||||
|  | @ -659,8 +667,10 @@ fn parse_sizes(sizes: &str) -> (u16, u16) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn download_icon(domain: &str) -> Result<(Bytes, Option<&str>), Error> { | async fn download_icon(domain: &str) -> Result<(Bytes, Option<&str>), Error> { | ||||||
|     if is_domain_blacklisted(domain).await { |     match check_domain_blacklist_reason(domain).await { | ||||||
|         err_silent!("Domain is blacklisted", domain) |         Some(DomainBlacklistReason::Regex) => err_silent!("Domain is blacklisted", domain), | ||||||
|  |         Some(DomainBlacklistReason::IP) => err_silent!("Host resolves to a non-global IP", domain), | ||||||
|  |         None => (), | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let icon_result = get_icon_url(domain).await?; |     let icon_result = get_icon_url(domain).await?; | ||||||
|  |  | ||||||
|  | @ -118,8 +118,8 @@ pub fn static_files(filename: String) -> Result<(ContentType, &'static [u8]), Er | ||||||
|         "jdenticon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jdenticon.js"))), |         "jdenticon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jdenticon.js"))), | ||||||
|         "datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))), |         "datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))), | ||||||
|         "datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))), |         "datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))), | ||||||
|         "jquery-3.6.2.slim.js" => { |         "jquery-3.6.3.slim.js" => { | ||||||
|             Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.2.slim.js"))) |             Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.3.slim.js"))) | ||||||
|         } |         } | ||||||
|         _ => err!(format!("Static file not found: {filename}")), |         _ => err!(format!("Static file not found: {filename}")), | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -141,6 +141,8 @@ macro_rules! make_config { | ||||||
|                 )+)+ |                 )+)+ | ||||||
|                 config.domain_set = _domain_set; |                 config.domain_set = _domain_set; | ||||||
| 
 | 
 | ||||||
|  |                 config.domain = config.domain.trim_end_matches('/').to_string(); | ||||||
|  | 
 | ||||||
|                 config.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase(); |                 config.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase(); | ||||||
|                 config.org_creation_users = config.org_creation_users.trim().to_lowercase(); |                 config.org_creation_users = config.org_creation_users.trim().to_lowercase(); | ||||||
| 
 | 
 | ||||||
|  | @ -1136,6 +1138,7 @@ where | ||||||
|     reg!("email/email_footer"); |     reg!("email/email_footer"); | ||||||
|     reg!("email/email_footer_text"); |     reg!("email/email_footer_text"); | ||||||
| 
 | 
 | ||||||
|  |     reg!("email/admin_reset_password", ".html"); | ||||||
|     reg!("email/change_email", ".html"); |     reg!("email/change_email", ".html"); | ||||||
|     reg!("email/delete_account", ".html"); |     reg!("email/delete_account", ".html"); | ||||||
|     reg!("email/emergency_access_invite_accepted", ".html"); |     reg!("email/emergency_access_invite_accepted", ".html"); | ||||||
|  |  | ||||||
|  | @ -287,40 +287,90 @@ impl Collection { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { |     pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { | ||||||
|         match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await { |         let user_uuid = user_uuid.to_string(); | ||||||
|             None => false, // Not in Org
 |  | ||||||
|             Some(user_org) => { |  | ||||||
|                 if user_org.has_full_access() { |  | ||||||
|                     return true; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|         db_run! { conn: { |         db_run! { conn: { | ||||||
|                     users_collections::table |             collections::table | ||||||
|                         .filter(users_collections::collection_uuid.eq(&self.uuid)) |             .left_join(users_collections::table.on( | ||||||
|                         .filter(users_collections::user_uuid.eq(user_uuid)) |                 users_collections::collection_uuid.eq(collections::uuid).and( | ||||||
|                         .filter(users_collections::read_only.eq(false)) |                     users_collections::user_uuid.eq(user_uuid.clone()) | ||||||
|  |                 ) | ||||||
|  |             )) | ||||||
|  |             .left_join(users_organizations::table.on( | ||||||
|  |                 collections::org_uuid.eq(users_organizations::org_uuid).and( | ||||||
|  |                     users_organizations::user_uuid.eq(user_uuid) | ||||||
|  |                 ) | ||||||
|  |             )) | ||||||
|  |             .left_join(groups_users::table.on( | ||||||
|  |                 groups_users::users_organizations_uuid.eq(users_organizations::uuid) | ||||||
|  |             )) | ||||||
|  |             .left_join(groups::table.on( | ||||||
|  |                 groups::uuid.eq(groups_users::groups_uuid) | ||||||
|  |             )) | ||||||
|  |             .left_join(collections_groups::table.on( | ||||||
|  |                 collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( | ||||||
|  |                     collections_groups::collections_uuid.eq(collections::uuid) | ||||||
|  |                 ) | ||||||
|  |             )) | ||||||
|  |             .filter(collections::uuid.eq(&self.uuid)) | ||||||
|  |             .filter( | ||||||
|  |                 users_collections::collection_uuid.eq(&self.uuid).and(users_collections::read_only.eq(false)).or(// Directly accessed collection
 | ||||||
|  |                     users_organizations::access_all.eq(true).or( // access_all in Organization
 | ||||||
|  |                         users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner
 | ||||||
|  |                 )).or( | ||||||
|  |                     groups::access_all.eq(true) // access_all in groups
 | ||||||
|  |                 ).or( // access via groups
 | ||||||
|  |                     groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( | ||||||
|  |                         collections_groups::collections_uuid.is_not_null().and( | ||||||
|  |                             collections_groups::read_only.eq(false)) | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|             .count() |             .count() | ||||||
|             .first::<i64>(conn) |             .first::<i64>(conn) | ||||||
|             .ok() |             .ok() | ||||||
|             .unwrap_or(0) != 0 |             .unwrap_or(0) != 0 | ||||||
|         }} |         }} | ||||||
|     } |     } | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { |     pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { | ||||||
|         match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await { |         let user_uuid = user_uuid.to_string(); | ||||||
|             None => true, // Not in Org
 |  | ||||||
|             Some(user_org) => { |  | ||||||
|                 if user_org.has_full_access() { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|         db_run! { conn: { |         db_run! { conn: { | ||||||
|                     users_collections::table |             collections::table | ||||||
|                         .filter(users_collections::collection_uuid.eq(&self.uuid)) |             .left_join(users_collections::table.on( | ||||||
|                         .filter(users_collections::user_uuid.eq(user_uuid)) |                 users_collections::collection_uuid.eq(collections::uuid).and( | ||||||
|                         .filter(users_collections::hide_passwords.eq(true)) |                     users_collections::user_uuid.eq(user_uuid.clone()) | ||||||
|  |                 ) | ||||||
|  |             )) | ||||||
|  |             .left_join(users_organizations::table.on( | ||||||
|  |                 collections::org_uuid.eq(users_organizations::org_uuid).and( | ||||||
|  |                     users_organizations::user_uuid.eq(user_uuid) | ||||||
|  |                 ) | ||||||
|  |             )) | ||||||
|  |             .left_join(groups_users::table.on( | ||||||
|  |                 groups_users::users_organizations_uuid.eq(users_organizations::uuid) | ||||||
|  |             )) | ||||||
|  |             .left_join(groups::table.on( | ||||||
|  |                 groups::uuid.eq(groups_users::groups_uuid) | ||||||
|  |             )) | ||||||
|  |             .left_join(collections_groups::table.on( | ||||||
|  |                 collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( | ||||||
|  |                     collections_groups::collections_uuid.eq(collections::uuid) | ||||||
|  |                 ) | ||||||
|  |             )) | ||||||
|  |             .filter(collections::uuid.eq(&self.uuid)) | ||||||
|  |             .filter( | ||||||
|  |                 users_collections::collection_uuid.eq(&self.uuid).and(users_collections::hide_passwords.eq(true)).or(// Directly accessed collection
 | ||||||
|  |                     users_organizations::access_all.eq(true).or( // access_all in Organization
 | ||||||
|  |                         users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner
 | ||||||
|  |                 )).or( | ||||||
|  |                     groups::access_all.eq(true) // access_all in groups
 | ||||||
|  |                 ).or( // access via groups
 | ||||||
|  |                     groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( | ||||||
|  |                         collections_groups::collections_uuid.is_not_null().and( | ||||||
|  |                             collections_groups::hide_passwords.eq(true)) | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|             .count() |             .count() | ||||||
|             .first::<i64>(conn) |             .first::<i64>(conn) | ||||||
|             .ok() |             .ok() | ||||||
|  | @ -328,8 +378,6 @@ impl Collection { | ||||||
|         }} |         }} | ||||||
|     } |     } | ||||||
| } | } | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| /// Database methods
 | /// Database methods
 | ||||||
| impl CollectionUser { | impl CollectionUser { | ||||||
|  |  | ||||||
|  | @ -87,9 +87,9 @@ pub enum EventType { | ||||||
|     OrganizationUserRemoved = 1503, |     OrganizationUserRemoved = 1503, | ||||||
|     OrganizationUserUpdatedGroups = 1504, |     OrganizationUserUpdatedGroups = 1504, | ||||||
|     // OrganizationUserUnlinkedSso = 1505, // Not supported
 |     // OrganizationUserUnlinkedSso = 1505, // Not supported
 | ||||||
|     // OrganizationUserResetPasswordEnroll = 1506, // Not supported
 |     OrganizationUserResetPasswordEnroll = 1506, | ||||||
|     // OrganizationUserResetPasswordWithdraw = 1507, // Not supported
 |     OrganizationUserResetPasswordWithdraw = 1507, | ||||||
|     // OrganizationUserAdminResetPassword = 1508, // Not supported
 |     OrganizationUserAdminResetPassword = 1508, | ||||||
|     // OrganizationUserResetSsoLink = 1509, // Not supported
 |     // OrganizationUserResetSsoLink = 1509, // Not supported
 | ||||||
|     // OrganizationUserFirstSsoLogin = 1510, // Not supported
 |     // OrganizationUserFirstSsoLogin = 1510, // Not supported
 | ||||||
|     OrganizationUserRevoked = 1511, |     OrganizationUserRevoked = 1511, | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ pub enum OrgPolicyType { | ||||||
|     PersonalOwnership = 5, |     PersonalOwnership = 5, | ||||||
|     DisableSend = 6, |     DisableSend = 6, | ||||||
|     SendOptions = 7, |     SendOptions = 7, | ||||||
|     // ResetPassword = 8, // Not supported
 |     ResetPassword = 8, | ||||||
|     // MaximumVaultTimeout = 9, // Not supported (Not AGPLv3 Licensed)
 |     // MaximumVaultTimeout = 9, // Not supported (Not AGPLv3 Licensed)
 | ||||||
|     // DisablePersonalVaultExport = 10, // Not supported (Not AGPLv3 Licensed)
 |     // DisablePersonalVaultExport = 10, // Not supported (Not AGPLv3 Licensed)
 | ||||||
| } | } | ||||||
|  | @ -44,6 +44,13 @@ pub struct SendOptionsPolicyData { | ||||||
|     pub DisableHideEmail: bool, |     pub DisableHideEmail: bool, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // https://github.com/bitwarden/server/blob/5cbdee137921a19b1f722920f0fa3cd45af2ef0f/src/Core/Models/Data/Organizations/Policies/ResetPasswordDataModel.cs
 | ||||||
|  | #[derive(Deserialize)] | ||||||
|  | #[allow(non_snake_case)] | ||||||
|  | pub struct ResetPasswordDataModel { | ||||||
|  |     pub AutoEnrollEnabled: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub type OrgPolicyResult = Result<(), OrgPolicyErr>; | pub type OrgPolicyResult = Result<(), OrgPolicyErr>; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
|  | @ -298,6 +305,20 @@ impl OrgPolicy { | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub async fn org_is_reset_password_auto_enroll(org_uuid: &str, conn: &mut DbConn) -> bool { | ||||||
|  |         match OrgPolicy::find_by_org_and_type(org_uuid, OrgPolicyType::ResetPassword, conn).await { | ||||||
|  |             Some(policy) => match serde_json::from_str::<UpCase<ResetPasswordDataModel>>(&policy.data) { | ||||||
|  |                 Ok(opts) => { | ||||||
|  |                     return opts.data.AutoEnrollEnabled; | ||||||
|  |                 } | ||||||
|  |                 _ => error!("Failed to deserialize ResetPasswordDataModel: {}", policy.data), | ||||||
|  |             }, | ||||||
|  |             None => return false, | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Returns true if the user belongs to an org that has enabled the `DisableHideEmail`
 |     /// Returns true if the user belongs to an org that has enabled the `DisableHideEmail`
 | ||||||
|     /// option of the `Send Options` policy, and the user is not an owner or admin of that org.
 |     /// option of the `Send Options` policy, and the user is not an owner or admin of that org.
 | ||||||
|     pub async fn is_hide_email_disabled(user_uuid: &str, conn: &mut DbConn) -> bool { |     pub async fn is_hide_email_disabled(user_uuid: &str, conn: &mut DbConn) -> bool { | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ db_object! { | ||||||
|         pub akey: String, |         pub akey: String, | ||||||
|         pub status: i32, |         pub status: i32, | ||||||
|         pub atype: i32, |         pub atype: i32, | ||||||
|  |         pub reset_password_key: Option<String>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -158,7 +159,7 @@ impl Organization { | ||||||
|             "SelfHost": true, |             "SelfHost": true, | ||||||
|             "UseApi": false, // Not supported
 |             "UseApi": false, // Not supported
 | ||||||
|             "HasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), |             "HasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), | ||||||
|             "UseResetPassword": false, // Not supported
 |             "UseResetPassword": CONFIG.mail_enabled(), | ||||||
| 
 | 
 | ||||||
|             "BusinessName": null, |             "BusinessName": null, | ||||||
|             "BusinessAddress1": null, |             "BusinessAddress1": null, | ||||||
|  | @ -194,6 +195,7 @@ impl UserOrganization { | ||||||
|             akey: String::new(), |             akey: String::new(), | ||||||
|             status: UserOrgStatus::Accepted as i32, |             status: UserOrgStatus::Accepted as i32, | ||||||
|             atype: UserOrgType::User as i32, |             atype: UserOrgType::User as i32, | ||||||
|  |             reset_password_key: None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -311,7 +313,8 @@ impl UserOrganization { | ||||||
|             "UseApi": false, // Not supported
 |             "UseApi": false, // Not supported
 | ||||||
|             "SelfHost": true, |             "SelfHost": true, | ||||||
|             "HasPublicAndPrivateKeys": org.private_key.is_some() && org.public_key.is_some(), |             "HasPublicAndPrivateKeys": org.private_key.is_some() && org.public_key.is_some(), | ||||||
|             "ResetPasswordEnrolled": false, // Not supported
 |             "ResetPasswordEnrolled": self.reset_password_key.is_some(), | ||||||
|  |             "UseResetPassword": CONFIG.mail_enabled(), | ||||||
|             "SsoBound": false, // Not supported
 |             "SsoBound": false, // Not supported
 | ||||||
|             "UseSso": false, // Not supported
 |             "UseSso": false, // Not supported
 | ||||||
|             "ProviderId": null, |             "ProviderId": null, | ||||||
|  | @ -377,6 +380,7 @@ impl UserOrganization { | ||||||
|             "Type": self.atype, |             "Type": self.atype, | ||||||
|             "AccessAll": self.access_all, |             "AccessAll": self.access_all, | ||||||
|             "TwoFactorEnabled": twofactor_enabled, |             "TwoFactorEnabled": twofactor_enabled, | ||||||
|  |             "ResetPasswordEnrolled":self.reset_password_key.is_some(), | ||||||
| 
 | 
 | ||||||
|             "Object": "organizationUserUserDetails", |             "Object": "organizationUserUserDetails", | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|  | @ -224,6 +224,7 @@ table! { | ||||||
|         akey -> Text, |         akey -> Text, | ||||||
|         status -> Integer, |         status -> Integer, | ||||||
|         atype -> Integer, |         atype -> Integer, | ||||||
|  |         reset_password_key -> Nullable<Text>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -224,6 +224,7 @@ table! { | ||||||
|         akey -> Text, |         akey -> Text, | ||||||
|         status -> Integer, |         status -> Integer, | ||||||
|         atype -> Integer, |         atype -> Integer, | ||||||
|  |         reset_password_key -> Nullable<Text>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -224,6 +224,7 @@ table! { | ||||||
|         akey -> Text, |         akey -> Text, | ||||||
|         status -> Integer, |         status -> Integer, | ||||||
|         atype -> Integer, |         atype -> Integer, | ||||||
|  |         reset_password_key -> Nullable<Text>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								src/mail.rs
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								src/mail.rs
									
										
									
									
									
								
							|  | @ -496,6 +496,19 @@ pub async fn send_test(address: &str) -> EmptyResult { | ||||||
|     send_email(address, &subject, body_html, body_text).await |     send_email(address, &subject, body_html, body_text).await | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pub async fn send_admin_reset_password(address: &str, user_name: &str, org_name: &str) -> EmptyResult { | ||||||
|  |     let (subject, body_html, body_text) = get_text( | ||||||
|  |         "email/admin_reset_password", | ||||||
|  |         json!({ | ||||||
|  |             "url": CONFIG.domain(), | ||||||
|  |             "img_src": CONFIG._smtp_img_src(), | ||||||
|  |             "user_name": user_name, | ||||||
|  |             "org_name": org_name, | ||||||
|  |         }), | ||||||
|  |     )?; | ||||||
|  |     send_email(address, &subject, body_html, body_text).await | ||||||
|  | } | ||||||
|  | 
 | ||||||
| async fn send_email(address: &str, subject: &str, body_html: String, body_text: String) -> EmptyResult { | async fn send_email(address: &str, subject: &str, body_html: String, body_text: String) -> EmptyResult { | ||||||
|     let smtp_from = &CONFIG.smtp_from(); |     let smtp_from = &CONFIG.smtp_from(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								src/static/scripts/admin.css
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								src/static/scripts/admin.css
									
										
									
									
										vendored
									
									
								
							|  | @ -18,24 +18,31 @@ img { | ||||||
|     border: var(--bs-alert-border); |     border: var(--bs-alert-border); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #users-table .vw-account-details { | ||||||
|  |     min-width: 250px; | ||||||
|  | } | ||||||
| #users-table .vw-created-at, #users-table .vw-last-active { | #users-table .vw-created-at, #users-table .vw-last-active { | ||||||
|     width: 85px; |     min-width: 85px; | ||||||
|     min-width: 70px; |     max-width: 85px; | ||||||
| } | } | ||||||
| #users-table .vw-items { | #users-table .vw-items, #orgs-table .vw-items, #orgs-table .vw-users { | ||||||
|     width: 35px; |  | ||||||
|     min-width: 35px; |     min-width: 35px; | ||||||
|  |     max-width: 40px; | ||||||
| } | } | ||||||
| #users-table .vw-organizations { | #users-table .vw-attachments, #orgs-table .vw-attachments { | ||||||
|     min-width: 120px; |     min-width: 100px; | ||||||
|  |     max-width: 130px; | ||||||
| } | } | ||||||
| #users-table .vw-actions, #orgs-table .vw-actions { | #users-table .vw-actions, #orgs-table .vw-actions { | ||||||
|     width: 130px; |  | ||||||
|     min-width: 130px; |     min-width: 130px; | ||||||
|  |     max-width: 130px; | ||||||
| } | } | ||||||
| #users-table .vw-org-cell { | #users-table .vw-org-cell { | ||||||
|     max-height: 120px; |     max-height: 120px; | ||||||
| } | } | ||||||
|  | #orgs-table .vw-org-details { | ||||||
|  |     min-width: 285px; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| #support-string { | #support-string { | ||||||
|     height: 16rem; |     height: 16rem; | ||||||
|  |  | ||||||
							
								
								
									
										26
									
								
								src/static/scripts/admin.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								src/static/scripts/admin.js
									
										
									
									
										vendored
									
									
								
							|  | @ -1,4 +1,6 @@ | ||||||
| "use strict"; | "use strict"; | ||||||
|  | /* eslint-env es2017, browser */ | ||||||
|  | /* exported BASE_URL, _post */ | ||||||
| 
 | 
 | ||||||
| function getBaseUrl() { | function getBaseUrl() { | ||||||
|     // If the base URL is `https://vaultwarden.example.com/base/path/`,
 |     // If the base URL is `https://vaultwarden.example.com/base/path/`,
 | ||||||
|  | @ -26,6 +28,8 @@ function msg(text, reload_page = true) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function _post(url, successMsg, errMsg, body, reload_page = true) { | function _post(url, successMsg, errMsg, body, reload_page = true) { | ||||||
|  |     let respStatus; | ||||||
|  |     let respStatusText; | ||||||
|     fetch(url, { |     fetch(url, { | ||||||
|         method: "POST", |         method: "POST", | ||||||
|         body: body, |         body: body, | ||||||
|  | @ -33,22 +37,30 @@ function _post(url, successMsg, errMsg, body, reload_page = true) { | ||||||
|         credentials: "same-origin", |         credentials: "same-origin", | ||||||
|         headers: { "Content-Type": "application/json" } |         headers: { "Content-Type": "application/json" } | ||||||
|     }).then( resp => { |     }).then( resp => { | ||||||
|         if (resp.ok) { msg(successMsg, reload_page); return Promise.reject({error: false}); } |         if (resp.ok) { | ||||||
|         const respStatus = resp.status; |             msg(successMsg, reload_page); | ||||||
|         const respStatusText = resp.statusText; |             // Abuse the catch handler by setting error to false and continue
 | ||||||
|  |             return Promise.reject({error: false}); | ||||||
|  |         } | ||||||
|  |         respStatus = resp.status; | ||||||
|  |         respStatusText = resp.statusText; | ||||||
|         return resp.text(); |         return resp.text(); | ||||||
|     }).then( respText => { |     }).then( respText => { | ||||||
|         try { |         try { | ||||||
|             const respJson = JSON.parse(respText); |             const respJson = JSON.parse(respText); | ||||||
|             return respJson ? respJson.ErrorModel.Message : "Unknown error"; |             if (respJson.ErrorModel && respJson.ErrorModel.Message) { | ||||||
|  |                 return respJson.ErrorModel.Message; | ||||||
|  |             } else { | ||||||
|  |                 return Promise.reject({body:`${respStatus} - ${respStatusText}\n\nUnknown error`, error: true}); | ||||||
|  |             } | ||||||
|         } catch (e) { |         } catch (e) { | ||||||
|             return Promise.reject({body:respStatus + " - " + respStatusText, error: true}); |             return Promise.reject({body:`${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true}); | ||||||
|         } |         } | ||||||
|     }).then( apiMsg => { |     }).then( apiMsg => { | ||||||
|         msg(errMsg + "\n" + apiMsg, reload_page); |         msg(`${errMsg}\n${apiMsg}`, reload_page); | ||||||
|     }).catch( e => { |     }).catch( e => { | ||||||
|         if (e.error === false) { return true; } |         if (e.error === false) { return true; } | ||||||
|         else { msg(errMsg + "\n" + e.body, reload_page); } |         else { msg(`${errMsg}\n${e.body}`, reload_page); } | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								src/static/scripts/admin_diagnostics.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								src/static/scripts/admin_diagnostics.js
									
										
									
									
										vendored
									
									
								
							|  | @ -1,4 +1,6 @@ | ||||||
| "use strict"; | "use strict"; | ||||||
|  | /* eslint-env es2017, browser */ | ||||||
|  | /* global BASE_URL:readable, BSN:readable */ | ||||||
| 
 | 
 | ||||||
| var dnsCheck = false; | var dnsCheck = false; | ||||||
| var timeCheck = false; | var timeCheck = false; | ||||||
|  | @ -65,7 +67,7 @@ function checkVersions(platform, installed, latest, commit=null) { | ||||||
| 
 | 
 | ||||||
| // ================================
 | // ================================
 | ||||||
| // Generate support string to be pasted on github or the forum
 | // Generate support string to be pasted on github or the forum
 | ||||||
| async function generateSupportString(dj) { | async function generateSupportString(event, dj) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|  | @ -114,7 +116,7 @@ async function generateSupportString(dj) { | ||||||
|     document.getElementById("copy-support").classList.remove("d-none"); |     document.getElementById("copy-support").classList.remove("d-none"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function copyToClipboard() { | function copyToClipboard(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|  | @ -208,12 +210,18 @@ function init(dj) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // onLoad events
 | // onLoad events
 | ||||||
| document.addEventListener("DOMContentLoaded", (/*event*/) => { | document.addEventListener("DOMContentLoaded", (event) => { | ||||||
|     const diag_json = JSON.parse(document.getElementById("diagnostics_json").innerText); |     const diag_json = JSON.parse(document.getElementById("diagnostics_json").innerText); | ||||||
|     init(diag_json); |     init(diag_json); | ||||||
| 
 | 
 | ||||||
|     document.getElementById("gen-support").addEventListener("click", () => { |     const btnGenSupport = document.getElementById("gen-support"); | ||||||
|         generateSupportString(diag_json); |     if (btnGenSupport) { | ||||||
|  |         btnGenSupport.addEventListener("click", () => { | ||||||
|  |             generateSupportString(event, diag_json); | ||||||
|         }); |         }); | ||||||
|     document.getElementById("copy-support").addEventListener("click", copyToClipboard); |     } | ||||||
|  |     const btnCopySupport = document.getElementById("copy-support"); | ||||||
|  |     if (btnCopySupport) { | ||||||
|  |         btnCopySupport.addEventListener("click", copyToClipboard); | ||||||
|  |     } | ||||||
| }); | }); | ||||||
							
								
								
									
										26
									
								
								src/static/scripts/admin_organizations.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								src/static/scripts/admin_organizations.js
									
										
									
									
										vendored
									
									
								
							|  | @ -1,6 +1,8 @@ | ||||||
| "use strict"; | "use strict"; | ||||||
|  | /* eslint-env es2017, browser, jquery */ | ||||||
|  | /* global _post:readable, BASE_URL:readable, reload:readable, jdenticon:readable */ | ||||||
| 
 | 
 | ||||||
| function deleteOrganization() { | function deleteOrganization(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const org_uuid = event.target.dataset.vwOrgUuid; |     const org_uuid = event.target.dataset.vwOrgUuid; | ||||||
|  | @ -28,9 +30,22 @@ function deleteOrganization() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function initActions() { | ||||||
|  |     document.querySelectorAll("button[vw-delete-organization]").forEach(btn => { | ||||||
|  |         btn.addEventListener("click", deleteOrganization); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (jdenticon) { | ||||||
|  |         jdenticon(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // onLoad events
 | // onLoad events
 | ||||||
| document.addEventListener("DOMContentLoaded", (/*event*/) => { | document.addEventListener("DOMContentLoaded", (/*event*/) => { | ||||||
|     jQuery("#orgs-table").DataTable({ |     jQuery("#orgs-table").DataTable({ | ||||||
|  |         "drawCallback": function() { | ||||||
|  |             initActions(); | ||||||
|  |         }, | ||||||
|         "stateSave": true, |         "stateSave": true, | ||||||
|         "responsive": true, |         "responsive": true, | ||||||
|         "lengthMenu": [ |         "lengthMenu": [ | ||||||
|  | @ -46,9 +61,10 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Add click events for organization actions
 |     // Add click events for organization actions
 | ||||||
|     document.querySelectorAll("button[vw-delete-organization]").forEach(btn => { |     initActions(); | ||||||
|         btn.addEventListener("click", deleteOrganization); |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|     document.getElementById("reload").addEventListener("click", reload); |     const btnReload = document.getElementById("reload"); | ||||||
|  |     if (btnReload) { | ||||||
|  |         btnReload.addEventListener("click", reload); | ||||||
|  |     } | ||||||
| }); | }); | ||||||
							
								
								
									
										55
									
								
								src/static/scripts/admin_settings.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								src/static/scripts/admin_settings.js
									
										
									
									
										vendored
									
									
								
							|  | @ -1,6 +1,8 @@ | ||||||
| "use strict"; | "use strict"; | ||||||
|  | /* eslint-env es2017, browser */ | ||||||
|  | /* global _post:readable, BASE_URL:readable */ | ||||||
| 
 | 
 | ||||||
| function smtpTest() { | function smtpTest(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     if (formHasChanges(config_form)) { |     if (formHasChanges(config_form)) { | ||||||
|  | @ -41,7 +43,7 @@ function getFormData() { | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function saveConfig() { | function saveConfig(event) { | ||||||
|     const data = JSON.stringify(getFormData()); |     const data = JSON.stringify(getFormData()); | ||||||
|     _post(`${BASE_URL}/admin/config/`, |     _post(`${BASE_URL}/admin/config/`, | ||||||
|         "Config saved correctly", |         "Config saved correctly", | ||||||
|  | @ -51,7 +53,7 @@ function saveConfig() { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function deleteConf() { | function deleteConf(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const input = prompt( |     const input = prompt( | ||||||
|  | @ -68,7 +70,7 @@ function deleteConf() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function backupDatabase() { | function backupDatabase(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     _post(`${BASE_URL}/admin/config/backup_db`, |     _post(`${BASE_URL}/admin/config/backup_db`, | ||||||
|  | @ -94,24 +96,26 @@ function formHasChanges(form) { | ||||||
| 
 | 
 | ||||||
| // This function will prevent submitting a from when someone presses enter.
 | // This function will prevent submitting a from when someone presses enter.
 | ||||||
| function preventFormSubmitOnEnter(form) { | function preventFormSubmitOnEnter(form) { | ||||||
|     form.onkeypress = function(e) { |     if (form) { | ||||||
|         const key = e.charCode || e.keyCode || 0; |         form.addEventListener("keypress", (event) => { | ||||||
|         if (key == 13) { |             if (event.key == "Enter") { | ||||||
|             e.preventDefault(); |                 event.preventDefault(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|     }; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // This function will hook into the smtp-test-email input field and will call the smtpTest() function when enter is pressed.
 | // This function will hook into the smtp-test-email input field and will call the smtpTest() function when enter is pressed.
 | ||||||
| function submitTestEmailOnEnter() { | function submitTestEmailOnEnter() { | ||||||
|     const smtp_test_email_input = document.getElementById("smtp-test-email"); |     const smtp_test_email_input = document.getElementById("smtp-test-email"); | ||||||
|     smtp_test_email_input.onkeypress = function(e) { |     if (smtp_test_email_input) { | ||||||
|         const key = e.charCode || e.keyCode || 0; |         smtp_test_email_input.addEventListener("keypress", (event) => { | ||||||
|         if (key == 13) { |             if (event.key == "Enter") { | ||||||
|             e.preventDefault(); |                 event.preventDefault(); | ||||||
|             smtpTest(); |                 smtpTest(event); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|     }; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Colorize some settings which are high risk
 | // Colorize some settings which are high risk
 | ||||||
|  | @ -124,11 +128,11 @@ function colorRiskSettings() { | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function toggleVis(evt) { | function toggleVis(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|     const elem = document.getElementById(evt.target.dataset.vwPwToggle); |     const elem = document.getElementById(event.target.dataset.vwPwToggle); | ||||||
|     const type = elem.getAttribute("type"); |     const type = elem.getAttribute("type"); | ||||||
|     if (type === "text") { |     if (type === "text") { | ||||||
|         elem.setAttribute("type", "password"); |         elem.setAttribute("type", "password"); | ||||||
|  | @ -146,10 +150,12 @@ function masterCheck(check_id, inputs_query) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const checkbox = document.getElementById(check_id); |     const checkbox = document.getElementById(check_id); | ||||||
|  |     if (checkbox) { | ||||||
|         const onChange = onChanged(checkbox, inputs_query); |         const onChange = onChanged(checkbox, inputs_query); | ||||||
|         onChange(); // Trigger the event initially
 |         onChange(); // Trigger the event initially
 | ||||||
|         checkbox.addEventListener("change", onChange); |         checkbox.addEventListener("change", onChange); | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| const config_form = document.getElementById("config-form"); | const config_form = document.getElementById("config-form"); | ||||||
| 
 | 
 | ||||||
|  | @ -172,9 +178,18 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { | ||||||
|         password_toggle_btn.addEventListener("click", toggleVis); |         password_toggle_btn.addEventListener("click", toggleVis); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     document.getElementById("backupDatabase").addEventListener("click", backupDatabase); |     const btnBackupDatabase = document.getElementById("backupDatabase"); | ||||||
|     document.getElementById("deleteConf").addEventListener("click", deleteConf); |     if (btnBackupDatabase) { | ||||||
|     document.getElementById("smtpTest").addEventListener("click", smtpTest); |         btnBackupDatabase.addEventListener("click", backupDatabase); | ||||||
|  |     } | ||||||
|  |     const btnDeleteConf = document.getElementById("deleteConf"); | ||||||
|  |     if (btnDeleteConf) { | ||||||
|  |         btnDeleteConf.addEventListener("click", deleteConf); | ||||||
|  |     } | ||||||
|  |     const btnSmtpTest = document.getElementById("smtpTest"); | ||||||
|  |     if (btnSmtpTest) { | ||||||
|  |         btnSmtpTest.addEventListener("click", smtpTest); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     config_form.addEventListener("submit", saveConfig); |     config_form.addEventListener("submit", saveConfig); | ||||||
| }); | }); | ||||||
							
								
								
									
										91
									
								
								src/static/scripts/admin_users.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										91
									
								
								src/static/scripts/admin_users.js
									
										
									
									
										vendored
									
									
								
							|  | @ -1,6 +1,8 @@ | ||||||
| "use strict"; | "use strict"; | ||||||
|  | /* eslint-env es2017, browser, jquery */ | ||||||
|  | /* global _post:readable, BASE_URL:readable, reload:readable, jdenticon:readable */ | ||||||
| 
 | 
 | ||||||
| function deleteUser() { | function deleteUser(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const id = event.target.parentNode.dataset.vwUserUuid; |     const id = event.target.parentNode.dataset.vwUserUuid; | ||||||
|  | @ -22,7 +24,7 @@ function deleteUser() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function remove2fa() { | function remove2fa(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const id = event.target.parentNode.dataset.vwUserUuid; |     const id = event.target.parentNode.dataset.vwUserUuid; | ||||||
|  | @ -36,7 +38,7 @@ function remove2fa() { | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function deauthUser() { | function deauthUser(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const id = event.target.parentNode.dataset.vwUserUuid; |     const id = event.target.parentNode.dataset.vwUserUuid; | ||||||
|  | @ -50,7 +52,7 @@ function deauthUser() { | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function disableUser() { | function disableUser(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const id = event.target.parentNode.dataset.vwUserUuid; |     const id = event.target.parentNode.dataset.vwUserUuid; | ||||||
|  | @ -68,7 +70,7 @@ function disableUser() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function enableUser() { | function enableUser(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const id = event.target.parentNode.dataset.vwUserUuid; |     const id = event.target.parentNode.dataset.vwUserUuid; | ||||||
|  | @ -86,7 +88,7 @@ function enableUser() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateRevisions() { | function updateRevisions(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     _post(`${BASE_URL}/admin/users/update_revision`, |     _post(`${BASE_URL}/admin/users/update_revision`, | ||||||
|  | @ -95,7 +97,7 @@ function updateRevisions() { | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function inviteUser() { | function inviteUser(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     const email = document.getElementById("inviteEmail"); |     const email = document.getElementById("inviteEmail"); | ||||||
|  | @ -182,7 +184,7 @@ userOrgTypeDialog.addEventListener("hide.bs.modal", function() { | ||||||
|     document.getElementById("userOrgTypeOrgUuid").value = ""; |     document.getElementById("userOrgTypeOrgUuid").value = ""; | ||||||
| }, false); | }, false); | ||||||
| 
 | 
 | ||||||
| function updateUserOrgType() { | function updateUserOrgType(event) { | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|  | @ -195,26 +197,7 @@ function updateUserOrgType() { | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // onLoad events
 | function initUserTable() { | ||||||
| document.addEventListener("DOMContentLoaded", (/*event*/) => { |  | ||||||
|     jQuery("#users-table").DataTable({ |  | ||||||
|         "stateSave": true, |  | ||||||
|         "responsive": true, |  | ||||||
|         "lengthMenu": [ |  | ||||||
|             [-1, 5, 10, 25, 50], |  | ||||||
|             ["All", 5, 10, 25, 50] |  | ||||||
|         ], |  | ||||||
|         "pageLength": -1, // Default show all
 |  | ||||||
|         "columnDefs": [{ |  | ||||||
|             "targets": [1, 2], |  | ||||||
|             "type": "date-iso" |  | ||||||
|         }, { |  | ||||||
|             "targets": 6, |  | ||||||
|             "searchable": false, |  | ||||||
|             "orderable": false |  | ||||||
|         }] |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     // Color all the org buttons per type
 |     // Color all the org buttons per type
 | ||||||
|     document.querySelectorAll("button[data-vw-org-type]").forEach(function(e) { |     document.querySelectorAll("button[data-vw-org-type]").forEach(function(e) { | ||||||
|         const orgType = ORG_TYPES[e.dataset.vwOrgType]; |         const orgType = ORG_TYPES[e.dataset.vwOrgType]; | ||||||
|  | @ -222,7 +205,6 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { | ||||||
|         e.title = orgType.name; |         e.title = orgType.name; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Add click events for user actions
 |  | ||||||
|     document.querySelectorAll("button[vw-remove2fa]").forEach(btn => { |     document.querySelectorAll("button[vw-remove2fa]").forEach(btn => { | ||||||
|         btn.addEventListener("click", remove2fa); |         btn.addEventListener("click", remove2fa); | ||||||
|     }); |     }); | ||||||
|  | @ -239,8 +221,51 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { | ||||||
|         btn.addEventListener("click", enableUser); |         btn.addEventListener("click", enableUser); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     document.getElementById("updateRevisions").addEventListener("click", updateRevisions); |     if (jdenticon) { | ||||||
|     document.getElementById("reload").addEventListener("click", reload); |         jdenticon(); | ||||||
|     document.getElementById("userOrgTypeForm").addEventListener("submit", updateUserOrgType); |     } | ||||||
|     document.getElementById("inviteUserForm").addEventListener("submit", inviteUser); | } | ||||||
|  | 
 | ||||||
|  | // onLoad events
 | ||||||
|  | document.addEventListener("DOMContentLoaded", (/*event*/) => { | ||||||
|  |     jQuery("#users-table").DataTable({ | ||||||
|  |         "drawCallback": function() { | ||||||
|  |             initUserTable(); | ||||||
|  |         }, | ||||||
|  |         "stateSave": true, | ||||||
|  |         "responsive": true, | ||||||
|  |         "lengthMenu": [ | ||||||
|  |             [-1, 2, 5, 10, 25, 50], | ||||||
|  |             ["All", 2, 5, 10, 25, 50] | ||||||
|  |         ], | ||||||
|  |         "pageLength": 2, // Default show all
 | ||||||
|  |         "columnDefs": [{ | ||||||
|  |             "targets": [1, 2], | ||||||
|  |             "type": "date-iso" | ||||||
|  |         }, { | ||||||
|  |             "targets": 6, | ||||||
|  |             "searchable": false, | ||||||
|  |             "orderable": false | ||||||
|  |         }] | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // Add click events for user actions
 | ||||||
|  |     initUserTable(); | ||||||
|  | 
 | ||||||
|  |     const btnUpdateRevisions = document.getElementById("updateRevisions"); | ||||||
|  |     if (btnUpdateRevisions) { | ||||||
|  |         btnUpdateRevisions.addEventListener("click", updateRevisions); | ||||||
|  |     } | ||||||
|  |     const btnReload = document.getElementById("reload"); | ||||||
|  |     if (btnReload) { | ||||||
|  |         btnReload.addEventListener("click", reload); | ||||||
|  |     } | ||||||
|  |     const btnUserOrgTypeForm = document.getElementById("userOrgTypeForm"); | ||||||
|  |     if (btnUserOrgTypeForm) { | ||||||
|  |         btnUserOrgTypeForm.addEventListener("submit", updateUserOrgType); | ||||||
|  |     } | ||||||
|  |     const btnInviteUserForm = document.getElementById("inviteUserForm"); | ||||||
|  |     if (btnInviteUserForm) { | ||||||
|  |         btnInviteUserForm.addEventListener("submit", inviteUser); | ||||||
|  |     } | ||||||
| }); | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| /*! | /*! | ||||||
|  * jQuery JavaScript Library v3.6.2 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector |  * jQuery JavaScript Library v3.6.3 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector | ||||||
|  * https://jquery.com/
 |  * https://jquery.com/
 | ||||||
|  * |  * | ||||||
|  * Includes Sizzle.js |  * Includes Sizzle.js | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
|  * Released under the MIT license |  * Released under the MIT license | ||||||
|  * https://jquery.org/license
 |  * https://jquery.org/license
 | ||||||
|  * |  * | ||||||
|  * Date: 2022-12-13T14:56Z |  * Date: 2022-12-20T21:28Z | ||||||
|  */ |  */ | ||||||
| ( function( global, factory ) { | ( function( global, factory ) { | ||||||
| 
 | 
 | ||||||
|  | @ -151,7 +151,7 @@ function toType( obj ) { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| var | var | ||||||
| 	version = "3.6.2 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", | 	version = "3.6.3 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", | ||||||
| 
 | 
 | ||||||
| 	// Define a local copy of jQuery
 | 	// Define a local copy of jQuery
 | ||||||
| 	jQuery = function( selector, context ) { | 	jQuery = function( selector, context ) { | ||||||
|  | @ -522,14 +522,14 @@ function isArrayLike( obj ) { | ||||||
| } | } | ||||||
| var Sizzle = | var Sizzle = | ||||||
| /*! | /*! | ||||||
|  * Sizzle CSS Selector Engine v2.3.8 |  * Sizzle CSS Selector Engine v2.3.9 | ||||||
|  * https://sizzlejs.com/
 |  * https://sizzlejs.com/
 | ||||||
|  * |  * | ||||||
|  * Copyright JS Foundation and other contributors |  * Copyright JS Foundation and other contributors | ||||||
|  * Released under the MIT license |  * Released under the MIT license | ||||||
|  * https://js.foundation/
 |  * https://js.foundation/
 | ||||||
|  * |  * | ||||||
|  * Date: 2022-11-16 |  * Date: 2022-12-19 | ||||||
|  */ |  */ | ||||||
| ( function( window ) { | ( function( window ) { | ||||||
| var i, | var i, | ||||||
|  | @ -890,7 +890,7 @@ function Sizzle( selector, context, results, seed ) { | ||||||
| 					if ( support.cssSupportsSelector && | 					if ( support.cssSupportsSelector && | ||||||
| 
 | 
 | ||||||
| 						// eslint-disable-next-line no-undef
 | 						// eslint-disable-next-line no-undef
 | ||||||
| 						!CSS.supports( "selector(" + newSelector + ")" ) ) { | 						!CSS.supports( "selector(:is(" + newSelector + "))" ) ) { | ||||||
| 
 | 
 | ||||||
| 						// Support: IE 11+
 | 						// Support: IE 11+
 | ||||||
| 						// Throw to get to the same code path as an error directly in qSA.
 | 						// Throw to get to the same code path as an error directly in qSA.
 | ||||||
|  | @ -1492,9 +1492,8 @@ setDocument = Sizzle.setDocument = function( node ) { | ||||||
| 		// `:has()` uses a forgiving selector list as an argument so our regular
 | 		// `:has()` uses a forgiving selector list as an argument so our regular
 | ||||||
| 		// `try-catch` mechanism fails to catch `:has()` with arguments not supported
 | 		// `try-catch` mechanism fails to catch `:has()` with arguments not supported
 | ||||||
| 		// natively like `:has(:contains("Foo"))`. Where supported & spec-compliant,
 | 		// natively like `:has(:contains("Foo"))`. Where supported & spec-compliant,
 | ||||||
| 		// we now use `CSS.supports("selector(SELECTOR_TO_BE_TESTED)")` but outside
 | 		// we now use `CSS.supports("selector(:is(SELECTOR_TO_BE_TESTED))")`, but
 | ||||||
| 		// that, let's mark `:has` as buggy to always use jQuery traversal for
 | 		// outside that we mark `:has` as buggy.
 | ||||||
| 		// `:has()`.
 |  | ||||||
| 		rbuggyQSA.push( ":has" ); | 		rbuggyQSA.push( ":has" ); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -5,10 +5,10 @@ | ||||||
|             <table id="orgs-table" class="table table-sm table-striped table-hover"> |             <table id="orgs-table" class="table table-sm table-striped table-hover"> | ||||||
|                 <thead> |                 <thead> | ||||||
|                     <tr> |                     <tr> | ||||||
|                         <th>Organization</th> |                         <th class="vw-org-details">Organization</th> | ||||||
|                         <th>Users</th> |                         <th class="vw-users">Users</th> | ||||||
|                         <th>Items</th> |                         <th class="vw-items">Items</th> | ||||||
|                         <th>Attachments</th> |                         <th class="vw-attachments">Attachments</th> | ||||||
|                         <th class="vw-actions">Actions</th> |                         <th class="vw-actions">Actions</th> | ||||||
|                     </tr> |                     </tr> | ||||||
|                 </thead> |                 </thead> | ||||||
|  | @ -38,7 +38,7 @@ | ||||||
|                             {{/if}} |                             {{/if}} | ||||||
|                         </td> |                         </td> | ||||||
|                         <td class="text-end px-0 small"> |                         <td class="text-end px-0 small"> | ||||||
|                             <button type="button" class="btn btn-sm btn-link p-0 border-0" vw-delete-organization data-vw-org-uuid="{{jsesc Id no_quote}}" data-vw-org-name="{{jsesc Name no_quote}}" data-vw-billing-email="{{jsesc BillingEmail no_quote}}">Delete Organization</button> |                             <button type="button" class="btn btn-sm btn-link p-0 border-0 float-right" vw-delete-organization data-vw-org-uuid="{{jsesc Id no_quote}}" data-vw-org-name="{{jsesc Name no_quote}}" data-vw-billing-email="{{jsesc BillingEmail no_quote}}">Delete Organization</button> | ||||||
|                         </td> |                         </td> | ||||||
|                     </tr> |                     </tr> | ||||||
|                     {{/each}} |                     {{/each}} | ||||||
|  | @ -53,7 +53,7 @@ | ||||||
| </main> | </main> | ||||||
| 
 | 
 | ||||||
| <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> | <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> | ||||||
| <script src="{{urlpath}}/vw_static/jquery-3.6.2.slim.js"></script> | <script src="{{urlpath}}/vw_static/jquery-3.6.3.slim.js"></script> | ||||||
| <script src="{{urlpath}}/vw_static/datatables.js"></script> | <script src="{{urlpath}}/vw_static/datatables.js"></script> | ||||||
| <script src="{{urlpath}}/vw_static/admin_organizations.js"></script> | <script src="{{urlpath}}/vw_static/admin_organizations.js"></script> | ||||||
| <script src="{{urlpath}}/vw_static/jdenticon.js"></script> | <script src="{{urlpath}}/vw_static/jdenticon.js"></script> | ||||||
|  |  | ||||||
|  | @ -47,7 +47,7 @@ | ||||||
|                             <div class="row my-2 align-items-center pt-3 border-top" title="Send a test email to given email address"> |                             <div class="row my-2 align-items-center pt-3 border-top" title="Send a test email to given email address"> | ||||||
|                                 <label for="smtp-test-email" class="col-sm-3 col-form-label">Test SMTP</label> |                                 <label for="smtp-test-email" class="col-sm-3 col-form-label">Test SMTP</label> | ||||||
|                                 <div class="col-sm-8 input-group"> |                                 <div class="col-sm-8 input-group"> | ||||||
|                                     <input class="form-control" id="smtp-test-email" type="email" placeholder="Enter test email" required> |                                     <input class="form-control" id="smtp-test-email" type="email" placeholder="Enter test email" required spellcheck="false"> | ||||||
|                                     <button type="button" class="btn btn-outline-primary input-group-text" id="smtpTest">Send test email</button> |                                     <button type="button" class="btn btn-outline-primary input-group-text" id="smtpTest">Send test email</button> | ||||||
|                                     <div class="invalid-tooltip">Please provide a valid email address</div> |                                     <div class="invalid-tooltip">Please provide a valid email address</div> | ||||||
|                                 </div> |                                 </div> | ||||||
|  | @ -85,7 +85,7 @@ | ||||||
|                                     <input readonly class="form-control" id="input_{{name}}" type="password" value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}}> |                                     <input readonly class="form-control" id="input_{{name}}" type="password" value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}}> | ||||||
|                                     <button class="btn btn-outline-secondary" type="button" data-vw-pw-toggle="input_{{name}}">Show/hide</button> |                                     <button class="btn btn-outline-secondary" type="button" data-vw-pw-toggle="input_{{name}}">Show/hide</button> | ||||||
|                                 {{else}} |                                 {{else}} | ||||||
|                                     <input readonly class="form-control" id="input_{{name}}" type="{{type}}" value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}}> |                                     <input readonly class="form-control" id="input_{{name}}" type="{{type}}" value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}} spellcheck="false"> | ||||||
|                                     {{#case type "password"}} |                                     {{#case type "password"}} | ||||||
|                                     <button class="btn btn-outline-secondary" type="button" data-vw-pw-toggle="input_{{name}}">Show/hide</button> |                                     <button class="btn btn-outline-secondary" type="button" data-vw-pw-toggle="input_{{name}}">Show/hide</button> | ||||||
|                                     {{/case}} |                                     {{/case}} | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
|             <table id="users-table" class="table table-sm table-striped table-hover"> |             <table id="users-table" class="table table-sm table-striped table-hover"> | ||||||
|                 <thead> |                 <thead> | ||||||
|                     <tr> |                     <tr> | ||||||
|                         <th>User</th> |                         <th class="vw-account-details">User</th> | ||||||
|                         <th class="vw-created-at">Created at</th> |                         <th class="vw-created-at">Created at</th> | ||||||
|                         <th class="vw-last-active">Last Active</th> |                         <th class="vw-last-active">Last Active</th> | ||||||
|                         <th class="vw-items">Items</th> |                         <th class="vw-items">Items</th> | ||||||
|  | @ -63,14 +63,14 @@ | ||||||
|                         <td class="text-end px-0 small"> |                         <td class="text-end px-0 small"> | ||||||
|                             <span data-vw-user-uuid="{{jsesc Id no_quote}}" data-vw-user-email="{{jsesc Email no_quote}}"> |                             <span data-vw-user-uuid="{{jsesc Id no_quote}}" data-vw-user-email="{{jsesc Email no_quote}}"> | ||||||
|                                 {{#if TwoFactorEnabled}} |                                 {{#if TwoFactorEnabled}} | ||||||
|                                 <button type="button" class="btn btn-sm btn-link p-0 border-0" vw-remove2fa>Remove all 2FA</button> |                                 <button type="button" class="btn btn-sm btn-link p-0 border-0 float-right" vw-remove2fa>Remove all 2FA</button> | ||||||
|                                 {{/if}} |                                 {{/if}} | ||||||
|                                 <button type="button" class="btn btn-sm btn-link p-0 border-0" vw-deauth-user>Deauthorize sessions</button> |                                 <button type="button" class="btn btn-sm btn-link p-0 border-0 float-right" vw-deauth-user>Deauthorize sessions</button> | ||||||
|                                 <button type="button" class="btn btn-sm btn-link p-0 border-0" vw-delete-user>Delete User</button> |                                 <button type="button" class="btn btn-sm btn-link p-0 border-0 float-right" vw-delete-user>Delete User</button> | ||||||
|                                 {{#if user_enabled}} |                                 {{#if user_enabled}} | ||||||
|                                 <button type="button" class="btn btn-sm btn-link p-0 border-0" vw-disable-user>Disable User</button> |                                 <button type="button" class="btn btn-sm btn-link p-0 border-0 float-right" vw-disable-user>Disable User</button> | ||||||
|                                 {{else}} |                                 {{else}} | ||||||
|                                 <button type="button" class="btn btn-sm btn-link p-0 border-0" vw-enable-user>Enable User</button> |                                 <button type="button" class="btn btn-sm btn-link p-0 border-0 float-right" vw-enable-user>Enable User</button> | ||||||
|                                 {{/if}} |                                 {{/if}} | ||||||
|                             </span> |                             </span> | ||||||
|                         </td> |                         </td> | ||||||
|  | @ -96,7 +96,7 @@ | ||||||
|             <small>Email:</small> |             <small>Email:</small> | ||||||
| 
 | 
 | ||||||
|             <form class="form-inline input-group w-50" id="inviteUserForm"> |             <form class="form-inline input-group w-50" id="inviteUserForm"> | ||||||
|                 <input type="email" class="form-control me-2" id="inviteEmail" placeholder="Enter email" required> |                 <input type="email" class="form-control me-2" id="inviteEmail" placeholder="Enter email" required spellcheck="false"> | ||||||
|                 <button type="submit" class="btn btn-primary">Invite</button> |                 <button type="submit" class="btn btn-primary">Invite</button> | ||||||
|             </form> |             </form> | ||||||
|         </div> |         </div> | ||||||
|  | @ -137,7 +137,7 @@ | ||||||
| </main> | </main> | ||||||
| 
 | 
 | ||||||
| <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> | <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> | ||||||
| <script src="{{urlpath}}/vw_static/jquery-3.6.2.slim.js"></script> | <script src="{{urlpath}}/vw_static/jquery-3.6.3.slim.js"></script> | ||||||
| <script src="{{urlpath}}/vw_static/datatables.js"></script> | <script src="{{urlpath}}/vw_static/datatables.js"></script> | ||||||
| <script src="{{urlpath}}/vw_static/admin_users.js"></script> | <script src="{{urlpath}}/vw_static/admin_users.js"></script> | ||||||
| <script src="{{urlpath}}/vw_static/jdenticon.js"></script> | <script src="{{urlpath}}/vw_static/jdenticon.js"></script> | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								src/static/templates/email/admin_reset_password.hbs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/static/templates/email/admin_reset_password.hbs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | Master Password Has Been Changed | ||||||
|  | <!----------------> | ||||||
|  | The master password for {{user_name}} has been changed by an administrator in your {{org_name}} organization. If you did not initiate this request, please reach out to your administrator immediately. | ||||||
|  | 
 | ||||||
|  | === | ||||||
|  | Github: https://github.com/dani-garcia/vaultwarden | ||||||
							
								
								
									
										11
									
								
								src/static/templates/email/admin_reset_password.html.hbs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/static/templates/email/admin_reset_password.html.hbs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | Master Password Has Been Changed | ||||||
|  | <!----------------> | ||||||
|  | {{> email/email_header }} | ||||||
|  | <table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> | ||||||
|  |     <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> | ||||||
|  |         <td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top"> | ||||||
|  |             The master password for <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{user_name}}</b> has been changed by an administrator in your <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{org_name}}</b> organization. If you did not initiate this request, please reach out to your administrator immediately. | ||||||
|  |         </td> | ||||||
|  |     </tr> | ||||||
|  | </table> | ||||||
|  | {{> email/email_footer }} | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue