Compare commits

..

2 Commits

Author SHA1 Message Date
Miroslav Prasil ebb30229f1 Add Image information 2018-06-01 15:17:09 +01:00
Miroslav Prasil 936af5431a Update readme for docker hub 2018-06-01 15:17:09 +01:00
49 changed files with 1405 additions and 4564 deletions
+5 -47
View File
@@ -1,55 +1,13 @@
## Bitwarden_RS Configuration File
## Uncomment any of the following lines to change the defaults
## Main data folder
# DATA_FOLDER=data
## Individual folders, these override %DATA_FOLDER%
# DATABASE_URL=data/db.sqlite3
# RSA_KEY_FILENAME=data/rsa_key
# PRIVATE_RSA_KEY=data/private_rsa_key.der
# PUBLIC_RSA_KEY=data/public_rsa_key.der
# ICON_CACHE_FOLDER=data/icon_cache
# ATTACHMENTS_FOLDER=data/attachments
## Web vault settings
# WEB_VAULT_FOLDER=web-vault/
# WEB_VAULT_ENABLED=true
# true for yes, anything else for no
SIGNUPS_ALLOWED=true
## Controls the WebSocket server port
# WEBSOCKET_PORT=3012
## Controls if new users can register
# SIGNUPS_ALLOWED=true
## Use a local favicon extractor
## Set to false to use bitwarden's official icon servers
## Set to true to use the local version, which is not as smart,
## but it doesn't send the cipher domains to bitwarden's servers
# LOCAL_ICON_EXTRACTOR=false
## Controls the PBBKDF password iterations to apply on the server
## The change only applies when the password is changed
# PASSWORD_ITERATIONS=100000
## Whether password hint should be sent into the error response when the client request it
# SHOW_PASSWORD_HINT=true
## Domain settings
## The domain must match the address from where you access the server
## Unless you are using U2F, or having problems with attachments not downloading, there is no need to change this
## For U2F to work, the server must use HTTPS, you can use Let's Encrypt for free certs
# DOMAIN=https://bw.domain.tld:8443
## Rocket specific settings, check Rocket documentation to learn more
# ROCKET_ENV=staging
# ROCKET_ENV=production
# ROCKET_ADDRESS=0.0.0.0 # Enable this to test mobile app
# ROCKET_PORT=8000
# ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"}
## Mail specific settings, set SMTP_HOST and SMTP_FROM to enable the mail service.
## Note: if SMTP_USERNAME is specified, SMTP_PASSWORD is mandatory
# SMTP_HOST=smtp.domain.tld
# SMTP_FROM=bitwarden-rs@domain.tld
# SMTP_PORT=587
# SMTP_SSL=true
# SMTP_USERNAME=username
# SMTP_PASSWORD=password
-7
View File
@@ -1,7 +0,0 @@
# Copied from Rocket's .travis.yml
language: rust
sudo: required # so we get a VM with higher specs
dist: trusty # so we get a VM with higher specs
cache: cargo
rust:
- nightly
+37 -38
View File
@@ -1,55 +1,56 @@
# Build instructions
## How to compile bitwarden_rs
Install `rust nightly`, in Windows the recommended way is through `rustup`.
## Dependencies
- `Rust nightly` (strongly recommended to use [rustup](https://rustup.rs/))
- `OpenSSL` (should be available in path, install through your system's package manager or use the [prebuilt binaries](https://wiki.openssl.org/index.php/Binaries))
- `NodeJS` (required to build the web-vault, (install through your system's package manager or use the [prebuilt binaries](https://nodejs.org/en/download/))
Install the `openssl` library, in Windows the best option is Microsoft's `vcpkg`,
on other systems use their respective package managers.
## Run/Compile
Then run:
```sh
# Compile and run
cargo run
# or just compile (binary located in target/release/bitwarden_rs)
cargo build --release
# or
cargo build
```
When run, the server is accessible in [http://localhost:80](http://localhost:80).
## How to install the web-vault locally
If you're using docker image, you can just update `VAULT_VERSION` variable in Dockerfile and rebuild the image.
### Install the web-vault
Clone the git repository at [bitwarden/web](https://github.com/bitwarden/web) and checkout the latest release tag (e.g. v2.1.1):
```sh
# clone the repository
Install `node.js` and either `yarn` or `npm` (usually included with node)
Clone the web-vault outside the project:
```
git clone https://github.com/bitwarden/web.git web-vault
cd web-vault
# switch to the latest tag
git checkout "$(git tag | tail -n1)"
```
Apply the patch file from `docker/set-vault-baseurl.patch`:
```sh
# In the Vault repository directory
git apply /path/to/bitwarden_rs/docker/set-vault-baseurl.patch
Modify `web-vault/settings.Production.json` to look like this:
```json
{
"appSettings": {
"apiUri": "/api",
"identityUri": "/identity",
"iconsUri": "/icons",
"stripeKey": "",
"braintreeKey": ""
}
}
```
Then, build the Vault:
Then, run the following from the `web-vault` dir:
```sh
npm run sub:init
# With yarn (recommended)
yarn
yarn gulp dist:selfHosted
# With npm
npm install
npm run dist
npx gulp dist:selfHosted
```
Finally copy the contents of the `build` folder into the `bitwarden_rs/web-vault` folder.
Finally copy the contents of the `web-vault/dist` folder into the `bitwarden_rs/web-vault` folder.
# Configuration
The available configuration options are documented in the default `.env` file, and they can be modified by uncommenting the desired options in that file or by setting their respective environment variables. Look at the README file for the main configuration options available.
Note: the environment variables override the values set in the `.env` file.
## How to recreate database schemas (for developers)
## How to recreate database schemas
Install diesel-cli with cargo:
```sh
cargo install diesel_cli --no-default-features --features sqlite-bundled
cargo install diesel_cli --no-default-features --features sqlite-bundled # Or use only sqlite to use the system version
```
Make sure that the correct path to the database is in the `.env` file.
@@ -62,9 +63,7 @@ diesel migration generate <name>
Modify the *.sql files, making sure that any changes are reverted in the down.sql file.
Apply the migrations and save the generated schemas as follows:
```sh
diesel migration redo
# This step should be done automatically when using diesel-cli > 1.3.0
# diesel print-schema > src/db/schema.rs
```
diesel migration redo
diesel print-schema > src/db/schema.rs
```
Generated
+632 -1048
View File
File diff suppressed because it is too large Load Diff
+16 -48
View File
@@ -1,49 +1,40 @@
[package]
name = "bitwarden_rs"
version = "1.0.0"
version = "0.9.0"
authors = ["Daniel García <dani-garcia@users.noreply.github.com>"]
[dependencies]
# Web framework for nightly with a focus on ease-of-use, expressibility, and speed.
rocket = { version = "0.3.16", features = ["tls"] }
rocket_codegen = "0.3.16"
rocket_contrib = "0.3.16"
rocket = { version = "0.3.12", features = ["tls"] }
rocket_codegen = "0.3.12"
rocket_contrib = "0.3.12"
# HTTP client
reqwest = "0.9.0"
reqwest = "0.8.6"
# multipart/form-data support
multipart = "0.15.3"
# WebSockets library
ws = "0.7.8"
# MessagePack library
rmpv = "0.4.0"
# Concurrent hashmap implementation
chashmap = "2.2.0"
multipart = "0.14.2"
# A generic serialization/deserialization framework
serde = "1.0.79"
serde_derive = "1.0.79"
serde_json = "1.0.28"
serde = "1.0.64"
serde_derive = "1.0.64"
serde_json = "1.0.19"
# A safe, extensible ORM and Query builder
diesel = { version = "1.3.3", features = ["sqlite", "chrono", "r2d2"] }
diesel_migrations = { version = "1.3.0", features = ["sqlite"] }
diesel = { version = "~1.2.2", features = ["sqlite", "chrono", "r2d2"] }
diesel_migrations = { version = "~1.2.0", features = ["sqlite"] }
# Bundled SQLite
libsqlite3-sys = { version = "0.9.3", features = ["bundled"] }
libsqlite3-sys = { version = "0.9.1", features = ["bundled"] }
# Crypto library
ring = { version = "= 0.11.0", features = ["rsa_signing"] }
# UUID generation
uuid = { version = "0.7.1", features = ["v4"] }
uuid = { version = "0.6.5", features = ["v4"] }
# Date and time library for Rust
chrono = "0.4.6"
chrono = "0.4.2"
# TOTP library
oath = "0.10.2"
@@ -54,34 +45,11 @@ data-encoding = "2.1.1"
# JWT library
jsonwebtoken = "= 4.0.1"
# U2F library
u2f = "0.1.2"
# A `dotenv` implementation for Rust
dotenv = { version = "0.13.0", default-features = false }
# Lazy static macro
lazy_static = "1.1.0"
# Numerical libraries
num-traits = "0.2.6"
num-derive = "0.2.2"
# Email libraries
lettre = "0.9.0"
lettre_email = "0.9.0"
native-tls = "0.2.1"
fast_chemail = "0.9.5"
# Number encoding library
byteorder = "1.2.6"
lazy_static = "1.0.1"
[patch.crates-io]
# Make jwt use ring 0.11, to match rocket
jsonwebtoken = { path = "libs/jsonwebtoken" }
rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' }
lettre = { git = 'https://github.com/lettre/lettre', rev = 'fc91bb6ee8f9a' }
lettre_email = { git = 'https://github.com/lettre/lettre', rev = 'fc91bb6ee8f9a' }
# Version 0.1.2 from crates.io lacks a commit that fixes a certificate error
u2f = { git = 'https://github.com/wisespace-io/u2f-rs', rev = '193de35093a44' }
jsonwebtoken = { path = "libs/jsonwebtoken" } # Make jwt use ring 0.11, to match rocket
+20 -21
View File
@@ -2,32 +2,36 @@
# https://docs.docker.com/develop/develop-images/multistage-build/
# https://whitfin.io/speeding-up-rust-docker-builds/
####################### VAULT BUILD IMAGE #######################
FROM node:8-alpine as vault
FROM node:9-alpine as vault
ENV VAULT_VERSION "v2.3.0"
ENV URL "https://github.com/bitwarden/web.git"
ENV VAULT_VERSION "1.26.0"
ENV URL "https://github.com/bitwarden/web/archive/v${VAULT_VERSION}.tar.gz"
RUN apk add --update-cache --upgrade \
curl \
git \
tar
tar \
&& npm install -g \
gulp-cli \
gulp
RUN mkdir /web-build \
&& cd /web-build \
&& curl -L "${URL}" | tar -xvz --strip-components=1
RUN git clone -b $VAULT_VERSION --depth 1 $URL web-build
WORKDIR /web-build
COPY /docker/set-vault-baseurl.patch /web-build/
RUN git apply set-vault-baseurl.patch
COPY /docker/settings.Production.json /web-build/
RUN npm run sub:init && npm install
RUN npm run dist \
&& mv build /web-vault
RUN git config --global url."https://github.com/".insteadOf ssh://git@github.com/ \
&& npm install \
&& gulp dist:selfHosted \
&& mv dist /web-vault
########################## BUILD IMAGE ##########################
# We need to use the Rust build image, because
# we need the Rust compiler and Cargo tooling
FROM rust as build
FROM rustlang/rust:nightly as build
# Using bundled SQLite, no need to install it
# RUN apt-get update && apt-get install -y\
@@ -42,7 +46,6 @@ WORKDIR /app
# Copies over *only* your manifests and vendored dependencies
COPY ./Cargo.* ./
COPY ./libs ./libs
COPY ./rust-toolchain ./rust-toolchain
# Builds your dependencies and removes the
# dummy project, except the target folder
@@ -63,27 +66,23 @@ RUN cargo build --release
# because we already have a binary built
FROM debian:stretch-slim
ENV ROCKET_ENV "staging"
ENV ROCKET_WORKERS=10
# Install needed libraries
RUN apt-get update && apt-get install -y\
openssl\
ca-certificates\
--no-install-recommends\
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /data
VOLUME /data
EXPOSE 80
EXPOSE 3012
# Copies the files from the context (env file and web-vault)
# and the binary from the "build" stage to the current stage
COPY .env .
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build app/target/release/bitwarden_rs .
# Configures the startup!
CMD ./bitwarden_rs
# Use production to disable Rocket logging
#CMD ROCKET_ENV=production ./bitwarden_rs
CMD ROCKET_ENV=staging ./bitwarden_rs
-81
View File
@@ -1,81 +0,0 @@
# Using multistage build:
# https://docs.docker.com/develop/develop-images/multistage-build/
# https://whitfin.io/speeding-up-rust-docker-builds/
####################### VAULT BUILD IMAGE #######################
FROM node:8-alpine as vault
ENV VAULT_VERSION "v2.3.0"
ENV URL "https://github.com/bitwarden/web.git"
RUN apk add --update-cache --upgrade \
curl \
git \
tar
RUN git clone -b $VAULT_VERSION --depth 1 $URL web-build
WORKDIR /web-build
COPY /docker/set-vault-baseurl.patch /web-build/
RUN git apply set-vault-baseurl.patch
RUN npm run sub:init && npm install
RUN npm run dist \
&& mv build /web-vault
########################## BUILD IMAGE ##########################
# Musl build image for statically compiled binary
FROM clux/muslrust:nightly-2018-08-24 as build
# Creates a dummy project used to grab dependencies
RUN USER=root cargo init --bin
# Copies over *only* your manifests and vendored dependencies
COPY ./Cargo.* ./
COPY ./libs ./libs
COPY ./rust-toolchain ./rust-toolchain
# Builds your dependencies and removes the
# dummy project, except the target folder
# This folder contains the compiled dependencies
RUN cargo build --release
RUN find . -not -path "./target*" -delete
# Copies the complete project
# To avoid copying unneeded files, use .dockerignore
COPY . .
# Builds again, this time it'll just be
# your actual source files being built
RUN cargo build --release
######################## RUNTIME IMAGE ########################
# Create a new stage with a minimal image
# because we already have a binary built
FROM alpine:3.8
ENV ROCKET_ENV "staging"
ENV ROCKET_WORKERS=10
ENV SSL_CERT_DIR=/etc/ssl/certs
# Install needed libraries
RUN apk add \
openssl\
ca-certificates \
&& rm /var/cache/apk/*
RUN mkdir /data
VOLUME /data
EXPOSE 80
EXPOSE 3012
# Copies the files from the context (env file and web-vault)
# and the binary from the "build" stage to the current stage
COPY .env .
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /volume/target/x86_64-unknown-linux-musl/release/bitwarden_rs .
# Configures the startup!
CMD ./bitwarden_rs
-113
View File
@@ -1,113 +0,0 @@
# Using multistage build:
# https://docs.docker.com/develop/develop-images/multistage-build/
# https://whitfin.io/speeding-up-rust-docker-builds/
####################### VAULT BUILD IMAGE #######################
FROM node:8-alpine as vault
ENV VAULT_VERSION "v2.3.0"
ENV URL "https://github.com/bitwarden/web.git"
RUN apk add --update-cache --upgrade \
curl \
git \
tar
RUN git clone -b $VAULT_VERSION --depth 1 $URL web-build
WORKDIR /web-build
COPY /docker/set-vault-baseurl.patch /web-build/
RUN git apply set-vault-baseurl.patch
RUN npm run sub:init && npm install
RUN npm run dist \
&& mv build /web-vault
########################## BUILD IMAGE ##########################
# We need to use the Rust build image, because
# we need the Rust compiler and Cargo tooling
FROM rust as build
RUN apt-get update \
&& apt-get install -y \
gcc-arm-linux-gnueabihf \
&& mkdir -p ~/.cargo \
&& echo '[target.armv7-unknown-linux-gnueabihf]' >> ~/.cargo/config \
&& echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config
ENV CARGO_HOME "/root/.cargo"
ENV USER "root"
# Creates a dummy project used to grab dependencies
RUN USER=root cargo new --bin app
WORKDIR /app
# Copies over *only* your manifests and vendored dependencies
COPY ./Cargo.* ./
COPY ./libs ./libs
COPY ./rust-toolchain ./rust-toolchain
# Prepare openssl armhf libs
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
/etc/apt/sources.list.d/deb-src.list \
&& dpkg --add-architecture armhf \
&& apt-get update \
&& apt-get install -y \
libssl-dev:armhf \
libc6-dev:armhf
ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc"
ENV CROSS_COMPILE="1"
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf"
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
# Builds your dependencies and removes the
# dummy project, except the target folder
# This folder contains the compiled dependencies
COPY . .
RUN rustup target add armv7-unknown-linux-gnueabihf
RUN cargo build --release --target=armv7-unknown-linux-gnueabihf -v
RUN find . -not -path "./target*" -delete
# Copies the complete project
# To avoid copying unneeded files, use .dockerignore
COPY . .
# Builds again, this time it'll just be
# your actual source files being built
RUN cargo build --release --target=armv7-unknown-linux-gnueabihf -v
######################## RUNTIME IMAGE ########################
# Create a new stage with a minimal image
# because we already have a binary built
FROM resin/armv7hf-debian:stretch
ENV ROCKET_ENV "staging"
ENV ROCKET_WORKERS=10
RUN [ "cross-build-start" ]
# Install needed libraries
RUN apt-get update && apt-get install -y\
openssl\
ca-certificates\
--no-install-recommends\
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /data
RUN [ "cross-build-end" ]
VOLUME /data
EXPOSE 80
# Copies the files from the context (env file and web-vault)
# and the binary from the "build" stage to the current stage
COPY .env .
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/armv7-unknown-linux-gnueabihf/release/bitwarden_rs .
# Configures the startup!
CMD ./bitwarden_rs
-95
View File
@@ -1,95 +0,0 @@
# Proxy examples
In this document, `<SERVER>` refers to the IP or domain where bitwarden_rs is accessible from. If both the proxy and bitwarden_rs are running in the same system, simply use `localhost`.
The ports proxied by default are `80` for the web server and `3012` for the WebSocket server. The proxies are configured to listen in port `443` with HTTPS enabled, which is recommended.
When using a proxy, it's preferrable to configure HTTPS at the proxy level and not at the application level, this way the WebSockets connection is also secured.
## Caddy
```nginx
localhost:443 {
# The negotiation endpoint is also proxied to Rocket
proxy /notifications/hub/negotiate <SERVER>:80 {
transparent
}
# Notifications redirected to the websockets server
proxy /notifications/hub <SERVER>:3012 {
websocket
}
# Proxy the Root directory to Rocket
proxy / <SERVER>:80 {
transparent
}
tls ${SSLCERTIFICATE} ${SSLKEY}
}
```
## Nginx (by shauder)
```nginx
server {
include conf.d/ssl/ssl.conf;
listen 443 ssl http2;
server_name vault.*;
location /notifications/hub/negotiate {
include conf.d/proxy-confs/proxy.conf;
proxy_pass http://<SERVER>:80;
}
location / {
include conf.d/proxy-confs/proxy.conf;
proxy_pass http://<SERVER>:80;
}
location /notifications/hub {
proxy_pass http://<SERVER>:3012;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
## Apache (by fbartels)
```apache
<VirtualHost *:443>
SSLEngine on
ServerName bitwarden.$hostname.$domainname
SSLCertificateFile ${SSLCERTIFICATE}
SSLCertificateKeyFile ${SSLKEY}
SSLCACertificateFile ${SSLCA}
${SSLCHAIN}
ErrorLog \${APACHE_LOG_DIR}/bitwarden-error.log
CustomLog \${APACHE_LOG_DIR}/bitwarden-access.log combined
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://<SERVER>:3012/$1 [P,L]
ProxyPass / http://<SERVER>:80/
ProxyPreserveHost On
ProxyRequests Off
</VirtualHost>
```
## Traefik (docker-compose example)
```traefik
labels:
- 'traefik.frontend.rule=Host:vault.example.local'
- 'traefik.docker.network=traefik'
- 'traefik.port=80'
- 'traefik.enable=true'
- 'traefik.web.frontend.rule=Host:vault.example.local'
- 'traefik.web.port=80'
- 'traefik.hub.frontend.rule=Path:/notifications/hub'
- 'traefik.hub.port=3012'
- 'traefik.negotiate.frontend.rule=Path:/notifications/hub/negotiate'
- 'traefik.negotiate.port=80'
```
+3 -313
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,2 +0,0 @@
[global.limits]
json = 10485760 # 10 MiB
-5
View File
@@ -1,5 +0,0 @@
# For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/db/schema.rs"
-28
View File
@@ -1,28 +0,0 @@
--- a/src/app/services/services.module.ts
+++ b/src/app/services/services.module.ts
@@ -120,20 +120,17 @@ const notificationsService = new NotificationsService(userService, syncService,
const environmentService = new EnvironmentService(apiService, storageService, notificationsService);
const auditService = new AuditService(cryptoFunctionService, apiService);
-const analytics = new Analytics(window, () => platformUtilsService.isDev() || platformUtilsService.isSelfHost(),
+const analytics = new Analytics(window, () => platformUtilsService.isDev() || platformUtilsService.isSelfHost() || true,
platformUtilsService, storageService, appIdService);
containerService.attachToWindow(window);
export function initFactory(): Function {
return async () => {
await (storageService as HtmlStorageService).init();
- const isDev = platformUtilsService.isDev();
- if (!isDev && platformUtilsService.isSelfHost()) {
- environmentService.baseUrl = window.location.origin;
- } else {
- environmentService.notificationsUrl = isDev ? 'http://localhost:61840' :
- 'https://notifications.bitwarden.com'; // window.location.origin + '/notifications';
- }
+ const isDev = false;
+ environmentService.baseUrl = window.location.origin;
+ environmentService.notificationsUrl = window.location.origin + '/notifications';
+
await apiService.setUrls({
base: isDev ? null : window.location.origin,
api: isDev ? 'http://localhost:4000' : null,
+9
View File
@@ -0,0 +1,9 @@
{
"appSettings": {
"apiUri": "/api",
"identityUri": "/identity",
"iconsUri": "/icons",
"stripeKey": "",
"braintreeKey": ""
}
}
@@ -1,8 +0,0 @@
UPDATE users
SET totp_secret = (
SELECT twofactor.data FROM twofactor
WHERE twofactor.type = 0
AND twofactor.user_uuid = users.uuid
);
DROP TABLE twofactor;
@@ -1,15 +0,0 @@
CREATE TABLE twofactor (
uuid TEXT NOT NULL PRIMARY KEY,
user_uuid TEXT NOT NULL REFERENCES users (uuid),
type INTEGER NOT NULL,
enabled BOOLEAN NOT NULL,
data TEXT NOT NULL,
UNIQUE (user_uuid, type)
);
INSERT INTO twofactor (uuid, user_uuid, type, enabled, data)
SELECT lower(hex(randomblob(16))) , uuid, 0, 1, u.totp_secret FROM users u where u.totp_secret IS NOT NULL;
UPDATE users SET totp_secret = NULL; -- Instead of recreating the table, just leave the columns empty
@@ -1,3 +0,0 @@
ALTER TABLE ciphers
ADD COLUMN
password_history TEXT;
@@ -1 +0,0 @@
DROP TABLE invitations;
@@ -1,3 +0,0 @@
CREATE TABLE invitations (
email TEXT NOT NULL PRIMARY KEY
);
@@ -1,7 +0,0 @@
ALTER TABLE users
ADD COLUMN
client_kdf_type INTEGER NOT NULL DEFAULT 0; -- PBKDF2
ALTER TABLE users
ADD COLUMN
client_kdf_iter INTEGER NOT NULL DEFAULT 5000;
-1
View File
@@ -1 +0,0 @@
nightly-2018-09-12
+18 -192
View File
@@ -3,10 +3,10 @@ use rocket_contrib::Json;
use db::DbConn;
use db::models::*;
use api::{PasswordData, JsonResult, EmptyResult, JsonUpcase, NumberOrString};
use api::{PasswordData, JsonResult, EmptyResult, JsonUpcase};
use auth::Headers;
use fast_chemail::is_valid_email;
use mail;
use util;
use CONFIG;
@@ -14,9 +14,8 @@ use CONFIG;
#[allow(non_snake_case)]
struct RegisterData {
Email: String,
Kdf: Option<i32>,
KdfIterations: Option<i32>,
Key: String,
#[serde(deserialize_with = "util::upcase_deserialize")]
Keys: Option<KeysData>,
MasterPasswordHash: String,
MasterPasswordHint: Option<String>,
@@ -34,40 +33,15 @@ struct KeysData {
fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
let data: RegisterData = data.into_inner().data;
let mut user = match User::find_by_mail(&data.Email, &conn) {
Some(mut user) => {
if Invitation::take(&data.Email, &conn) {
for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() {
user_org.status = UserOrgStatus::Accepted as i32;
user_org.save(&conn);
};
user
} else if CONFIG.signups_allowed {
err!("Account with this email already exists")
} else {
err!("Registration not allowed")
}
},
None => {
if CONFIG.signups_allowed || Invitation::take(&data.Email, &conn) {
User::new(data.Email)
} else {
err!("Registration not allowed")
}
}
};
if let Some(client_kdf_iter) = data.KdfIterations {
user.client_kdf_iter = client_kdf_iter;
if !CONFIG.signups_allowed {
err!(format!("Signups not allowed"))
}
if let Some(client_kdf_type) = data.Kdf {
user.client_kdf_type = client_kdf_type;
if let Some(_) = User::find_by_mail(&data.Email, &conn) {
err!("Email already exists")
}
user.set_password(&data.MasterPasswordHash);
user.key = data.Key;
let mut user = User::new(data.Email, data.Key, data.MasterPasswordHash);
// Add extra fields if present
if let Some(name) = data.Name {
@@ -93,36 +67,6 @@ fn profile(headers: Headers, conn: DbConn) -> JsonResult {
Ok(Json(headers.user.to_json(&conn)))
}
#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
struct ProfileData {
#[serde(rename = "Culture")]
_Culture: String, // Ignored, always use en-US
MasterPasswordHint: Option<String>,
Name: String,
}
#[put("/accounts/profile", data = "<data>")]
fn put_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
post_profile(data, headers, conn)
}
#[post("/accounts/profile", data = "<data>")]
fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
let data: ProfileData = data.into_inner().data;
let mut user = headers.user;
user.name = data.Name;
user.password_hint = match data.MasterPasswordHint {
Some(ref h) if h.is_empty() => None,
_ => data.MasterPasswordHint,
};
user.save(&conn);
Ok(Json(user.to_json(&conn)))
}
#[get("/users/<uuid>/public-key")]
fn get_public_keys(uuid: String, _headers: Headers, conn: DbConn) -> JsonResult {
let user = match User::find_by_uuid(&uuid, &conn) {
@@ -175,35 +119,6 @@ fn post_password(data: JsonUpcase<ChangePassData>, headers: Headers, conn: DbCon
Ok(())
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct ChangeKdfData {
Kdf: i32,
KdfIterations: i32,
MasterPasswordHash: String,
NewMasterPasswordHash: String,
Key: String,
}
#[post("/accounts/kdf", data = "<data>")]
fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, conn: DbConn) -> EmptyResult {
let data: ChangeKdfData = data.into_inner().data;
let mut user = headers.user;
if !user.check_valid_password(&data.MasterPasswordHash) {
err!("Invalid password")
}
user.client_kdf_iter = data.KdfIterations;
user.client_kdf_type = data.Kdf;
user.set_password(&data.NewMasterPasswordHash);
user.key = data.Key;
user.save(&conn);
Ok(())
}
#[post("/accounts/security-stamp", data = "<data>")]
fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
let data: PasswordData = data.into_inner().data;
@@ -219,41 +134,15 @@ fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -
Ok(())
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct EmailTokenData {
MasterPasswordHash: String,
NewEmail: String,
}
#[post("/accounts/email-token", data = "<data>")]
fn post_email_token(data: JsonUpcase<EmailTokenData>, headers: Headers, conn: DbConn) -> EmptyResult {
let data: EmailTokenData = data.into_inner().data;
if !headers.user.check_valid_password(&data.MasterPasswordHash) {
err!("Invalid password")
}
if User::find_by_mail(&data.NewEmail, &conn).is_some() {
err!("Email already in use");
}
Ok(())
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct ChangeEmailData {
MasterPasswordHash: String,
NewEmail: String,
Key: String,
NewMasterPasswordHash: String,
#[serde(rename = "Token")]
_Token: NumberOrString,
}
#[post("/accounts/email", data = "<data>")]
#[post("/accounts/email-token", data = "<data>")]
fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn) -> EmptyResult {
let data: ChangeEmailData = data.into_inner().data;
let mut user = headers.user;
@@ -267,21 +156,12 @@ fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn)
}
user.email = data.NewEmail;
user.set_password(&data.NewMasterPasswordHash);
user.key = data.Key;
user.save(&conn);
Ok(())
}
#[post("/accounts/delete", data = "<data>")]
fn post_delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
delete_account(data, headers, conn)
}
#[delete("/accounts", data = "<data>")]
fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> EmptyResult {
let data: PasswordData = data.into_inner().data;
let user = headers.user;
@@ -292,15 +172,17 @@ fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn
// Delete ciphers and their attachments
for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
if cipher.delete(&conn).is_err() {
err!("Failed deleting cipher")
match cipher.delete(&conn) {
Ok(()) => (),
Err(_) => err!("Failed deleting cipher")
}
}
// Delete folders
for f in Folder::find_by_user(&user.uuid, &conn) {
if f.delete(&conn).is_err() {
err!("Failed deleting folder")
match f.delete(&conn) {
Ok(()) => (),
Err(_) => err!("Failed deleting folder")
}
}
@@ -315,62 +197,6 @@ fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn
#[get("/accounts/revision-date")]
fn revision_date(headers: Headers) -> String {
let revision_date = headers.user.updated_at.timestamp_millis();
let revision_date = headers.user.updated_at.timestamp();
revision_date.to_string()
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct PasswordHintData {
Email: String,
}
#[post("/accounts/password-hint", data = "<data>")]
fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResult {
let data: PasswordHintData = data.into_inner().data;
if !is_valid_email(&data.Email) {
err!("This email address is not valid...");
}
let user = User::find_by_mail(&data.Email, &conn);
if user.is_none() {
return Ok(());
}
let user = user.unwrap();
if let Some(ref mail_config) = CONFIG.mail {
if let Err(e) = mail::send_password_hint(&user.email, user.password_hint, mail_config) {
err!(format!("There have been a problem sending the email: {}", e));
}
} else if CONFIG.show_password_hint {
if let Some(hint) = user.password_hint {
err!(format!("Your password hint is: {}", &hint));
} else {
err!("Sorry, you have no password hint...");
}
}
Ok(())
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct PreloginData {
Email: String,
}
#[post("/accounts/prelogin", data = "<data>")]
fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> JsonResult {
let data: PreloginData = data.into_inner().data;
let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &conn) {
Some(user) => (user.client_kdf_type, user.client_kdf_iter),
None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT),
};
Ok(Json(json!({
"Kdf": kdf_type,
"KdfIterations": kdf_iter
})))
}
+139 -262
View File
File diff suppressed because it is too large Load Diff
+9 -15
View File
@@ -1,10 +1,9 @@
use rocket::State;
use rocket_contrib::{Json, Value};
use db::DbConn;
use db::models::*;
use api::{JsonResult, EmptyResult, JsonUpcase, WebSocketUsers, UpdateType};
use api::{JsonResult, EmptyResult, JsonUpcase};
use auth::Headers;
#[get("/folders")]
@@ -41,24 +40,23 @@ pub struct FolderData {
}
#[post("/folders", data = "<data>")]
fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn) -> JsonResult {
let data: FolderData = data.into_inner().data;
let mut folder = Folder::new(headers.user.uuid.clone(), data.Name);
folder.save(&conn);
ws.send_folder_update(UpdateType::SyncFolderCreate, &folder);
Ok(Json(folder.to_json()))
}
#[post("/folders/<uuid>", data = "<data>")]
fn post_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
put_folder(uuid, data, headers, conn, ws)
fn post_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn) -> JsonResult {
put_folder(uuid, data, headers, conn)
}
#[put("/folders/<uuid>", data = "<data>")]
fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> JsonResult {
fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn) -> JsonResult {
let data: FolderData = data.into_inner().data;
let mut folder = match Folder::find_by_uuid(&uuid, &conn) {
@@ -73,18 +71,17 @@ fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn
folder.name = data.Name;
folder.save(&conn);
ws.send_folder_update(UpdateType::SyncFolderUpdate, &folder);
Ok(Json(folder.to_json()))
}
#[post("/folders/<uuid>/delete")]
fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
delete_folder(uuid, headers, conn, ws)
fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn) -> EmptyResult {
delete_folder(uuid, headers, conn)
}
#[delete("/folders/<uuid>")]
fn delete_folder(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
fn delete_folder(uuid: String, headers: Headers, conn: DbConn) -> EmptyResult {
let folder = match Folder::find_by_uuid(&uuid, &conn) {
Some(folder) => folder,
_ => err!("Invalid folder")
@@ -96,10 +93,7 @@ fn delete_folder(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSock
// Delete the actual folder entry
match folder.delete(&conn) {
Ok(()) => {
ws.send_folder_update(UpdateType::SyncFolderDelete, &folder);
Ok(())
}
Ok(()) => Ok(()),
Err(_) => err!("Failed deleting folder")
}
}
-116
View File
@@ -646,121 +646,5 @@
"wiktionary.org"
],
"Excluded": false
},
{
"Type": 72,
"Domains": [
"airbnb.at",
"airbnb.be",
"airbnb.ca",
"airbnb.ch",
"airbnb.cl",
"airbnb.co.cr",
"airbnb.co.id",
"airbnb.co.in",
"airbnb.co.kr",
"airbnb.co.nz",
"airbnb.co.uk",
"airbnb.co.ve",
"airbnb.com",
"airbnb.com.ar",
"airbnb.com.au",
"airbnb.com.bo",
"airbnb.com.br",
"airbnb.com.bz",
"airbnb.com.co",
"airbnb.com.ec",
"airbnb.com.gt",
"airbnb.com.hk",
"airbnb.com.hn",
"airbnb.com.mt",
"airbnb.com.my",
"airbnb.com.ni",
"airbnb.com.pa",
"airbnb.com.pe",
"airbnb.com.py",
"airbnb.com.sg",
"airbnb.com.sv",
"airbnb.com.tr",
"airbnb.com.tw",
"airbnb.cz",
"airbnb.de",
"airbnb.dk",
"airbnb.es",
"airbnb.fi",
"airbnb.fr",
"airbnb.gr",
"airbnb.gy",
"airbnb.hu",
"airbnb.ie",
"airbnb.is",
"airbnb.it",
"airbnb.jp",
"airbnb.mx",
"airbnb.nl",
"airbnb.no",
"airbnb.pl",
"airbnb.pt",
"airbnb.ru",
"airbnb.se"
],
"Excluded": false
},
{
"Type": 73,
"Domains": [
"eventbrite.at",
"eventbrite.be",
"eventbrite.ca",
"eventbrite.ch",
"eventbrite.cl",
"eventbrite.co.id",
"eventbrite.co.in",
"eventbrite.co.kr",
"eventbrite.co.nz",
"eventbrite.co.uk",
"eventbrite.co.ve",
"eventbrite.com",
"eventbrite.com.au",
"eventbrite.com.bo",
"eventbrite.com.br",
"eventbrite.com.co",
"eventbrite.com.hk",
"eventbrite.com.hn",
"eventbrite.com.pe",
"eventbrite.com.sg",
"eventbrite.com.tr",
"eventbrite.com.tw",
"eventbrite.cz",
"eventbrite.de",
"eventbrite.dk",
"eventbrite.fi",
"eventbrite.fr",
"eventbrite.gy",
"eventbrite.hu",
"eventbrite.ie",
"eventbrite.is",
"eventbrite.it",
"eventbrite.jp",
"eventbrite.mx",
"eventbrite.nl",
"eventbrite.no",
"eventbrite.pl",
"eventbrite.pt",
"eventbrite.ru",
"eventbrite.se"
],
"Excluded": false
},
{
"Type": 74,
"Domains": [
"stackexchange.com",
"superuser.com",
"stackoverflow.com",
"serverfault.com",
"mathoverflow.net"
],
"Excluded": false
}
]
+10 -41
View File
@@ -2,7 +2,7 @@ mod accounts;
mod ciphers;
mod folders;
mod organizations;
pub(crate) mod two_factor;
mod two_factor;
use self::accounts::*;
use self::ciphers::*;
@@ -14,20 +14,13 @@ pub fn routes() -> Vec<Route> {
routes![
register,
profile,
put_profile,
post_profile,
get_public_keys,
post_keys,
post_password,
post_kdf,
post_sstamp,
post_email_token,
post_email,
delete_account,
post_delete_account,
revision_date,
password_hint,
prelogin,
sync,
@@ -36,31 +29,20 @@ pub fn routes() -> Vec<Route> {
get_cipher_admin,
get_cipher_details,
post_ciphers,
put_cipher_admin,
post_ciphers_admin,
post_ciphers_import,
post_attachment,
post_attachment_admin,
post_attachment_share,
delete_attachment_post,
delete_attachment_post_admin,
delete_attachment,
delete_attachment_admin,
post_cipher_admin,
post_cipher_share,
put_cipher_share,
put_cipher_share_seleted,
post_cipher,
put_cipher,
delete_cipher_post,
delete_cipher_post_admin,
delete_cipher,
delete_cipher_admin,
delete_cipher_selected,
delete_cipher_selected_post,
delete_all,
move_cipher_selected,
move_cipher_selected_put,
get_folders,
get_folder,
@@ -73,46 +55,31 @@ pub fn routes() -> Vec<Route> {
get_twofactor,
get_recover,
recover,
disable_twofactor,
disable_twofactor_put,
generate_authenticator,
activate_authenticator,
activate_authenticator_put,
generate_u2f,
activate_u2f,
activate_u2f_put,
disable_authenticator,
get_organization,
create_organization,
delete_organization,
post_delete_organization,
leave_organization,
get_user_collections,
get_org_collections,
get_org_collection_detail,
get_collection_users,
put_organization,
post_organization,
post_organization_collections,
delete_organization_collection_user,
post_organization_collection_delete_user,
post_organization_collection_update,
put_organization_collection_update,
delete_organization_collection,
post_organization_collection_delete,
post_collections_update,
post_collections_admin,
put_collections_admin,
get_org_details,
get_org_users,
send_invite,
confirm_invite,
get_user,
edit_user,
put_organization_user,
delete_user,
post_delete_user,
post_org_import,
clear_device_token,
put_device_token,
@@ -139,7 +106,8 @@ use auth::Headers;
#[put("/devices/identifier/<uuid>/clear-token", data = "<data>")]
fn clear_device_token(uuid: String, data: Json<Value>, headers: Headers, conn: DbConn) -> EmptyResult {
let _data: Value = data.into_inner();
println!("UUID: {:#?}", uuid);
println!("DATA: {:#?}", data);
let device = match Device::find_by_uuid(&uuid, &conn) {
Some(device) => device,
@@ -157,8 +125,9 @@ fn clear_device_token(uuid: String, data: Json<Value>, headers: Headers, conn: D
#[put("/devices/identifier/<uuid>/token", data = "<data>")]
fn put_device_token(uuid: String, data: Json<Value>, headers: Headers, conn: DbConn) -> JsonResult {
let _data: Value = data.into_inner();
println!("UUID: {:#?}", uuid);
println!("DATA: {:#?}", data);
let device = match Device::find_by_uuid(&uuid, &conn) {
Some(device) => device,
None => err!("Device not found")
@@ -181,7 +150,7 @@ struct GlobalDomain {
Excluded: bool,
}
const GLOBAL_DOMAINS: &str = include_str!("global_domains.json");
const GLOBAL_DOMAINS: &'static str = include_str!("global_domains.json");
#[get("/settings/domains")]
fn get_eq_domains(headers: Headers) -> JsonResult {
@@ -216,8 +185,8 @@ struct EquivDomainData {
fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> EmptyResult {
let data: EquivDomainData = data.into_inner().data;
let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default();
let equivalent_domains = data.EquivalentDomains.unwrap_or_default();
let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or(Vec::new());
let equivalent_domains = data.EquivalentDomains.unwrap_or(Vec::new());
let mut user = headers.user;
use serde_json::to_string;
File diff suppressed because it is too large Load Diff
+100 -399
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More