Compare commits

...

34 Commits

Author SHA1 Message Date
Daniel García
bd20d8724b Merge pull request #147 from mprasil/master
Bump version to 0.13.0 - latest Vault v1
2018-08-21 22:32:54 +02:00
Miroslav Prasil
df041108f6 Bump version to 0.13.0 - latest Vault v1 2018-08-21 21:13:56 +01:00
Daniel García
b6312340b6 Merge pull request #138 from mprasil/readme_updates
Cleaned up HTTPS example
2018-08-15 15:12:01 +02:00
Miroslav Prasil
80bad9f66d Cleaned up HTTPS example 2018-08-15 11:18:34 +01:00
Daniel García
5a55dd1d4b Merge pull request #133 from mprasil/document_differences
Extend documentation
2018-08-13 14:38:54 +02:00
Miroslav Prasil
c0f554311b Extend documentation 2018-08-13 12:01:52 +01:00
Daniel García
c6256e1455 Merge pull request #128 from mprasil/revision_date
Return revision date in miliseconds (fixes #127)
2018-08-10 19:40:56 +02:00
Daniel García
0cd3053fcb Merge pull request #125 from stammw/master
Make password hints available in the error message #85
2018-08-10 19:40:31 +02:00
Miroslav Prasil
58c1545707 Return revision date in miliseconds (fixes #127) 2018-08-10 17:18:59 +01:00
Jean-Christophe BEGUE
d3b4b10d18 Add a explaination to the password hint message #85 2018-08-10 16:59:23 +02:00
Jean-Christophe BEGUE
c031ae9f2f Make password hints available in the error message #85 2018-08-10 15:52:06 +02:00
Daniel García
56b3afa77c Merge pull request #107 from shauder/bug/attachments_for_orgs
Bug/attachments for orgs
2018-07-31 20:08:05 +02:00
Shane A. Faulkner
d335f45e34 Bump version to 0.12.0 2018-07-31 12:07:03 -05:00
Shane A. Faulkner
34d2648509 Merge pull request #3 from shauder/master
Sync working branch with changes in master upstream
2018-07-31 12:05:52 -05:00
Shane A. Faulkner
f39c4fe2f4 Merge pull request #2 from dani-garcia/master
Sync local fork with upstream
2018-07-31 12:03:39 -05:00
Shane A. Faulkner
01875c395b Merge pull request #1 from mprasil/concurrency_fix
WAL journal mode and delete retry added
2018-07-31 11:39:45 -05:00
Miroslav Prasil
2872f40d13 WAL journal mode and delete retry added 2018-07-31 16:43:43 +01:00
mprasil
d7df545078 Merge pull request #104 from jcgruenhage/patch-1
Update matrix.to link in the README
2018-07-26 22:52:12 +01:00
Jan Christian Grünhage
d073f06652 Update matrix.to link in the README
Using the room ID instead of an alias isn't supposed to be working for joining rooms, and doesn't work when joining over federation. It only works when your server is already participating in the room.
2018-07-26 22:42:02 +01:00
Daniel García
3726da9c14 Merge pull request #103 from mprasil/https_doc_fix
Fixed the documentation for https (resolves #101)
2018-07-24 15:28:23 +02:00
Miroslav Prasil
51450a0df9 Fixed the documentation for https (resolves #101) 2018-07-24 12:32:41 +01:00
Shane A. Faulkner
98bae4a0a1 Cleanup and working with 2 or less attachments 2018-07-18 15:35:45 -05:00
Daniel García
48e69cebab Merge pull request #92 from mprasil/not_found
Return 404 in case the path doesn't match instead of 500
2018-07-18 14:07:28 +02:00
Daniel García
798a3b6a43 Merge pull request #91 from mprasil/worker_threads
Change number of workers in image, document the setting (fixes #90)
2018-07-18 14:06:53 +02:00
Miroslav Prasil
2dc1427027 Bump the version 2018-07-18 12:04:48 +01:00
Miroslav Prasil
233d23a527 Return 404 in case the path doesn't match instead of 500 2018-07-18 11:54:33 +01:00
Miroslav Prasil
06f7bd7c97 Change number of workers in image, document the setting (fixes #90) 2018-07-18 10:41:39 +01:00
Daniel García
458a238c38 Merge pull request #89 from mprasil/unconfirmed_guard
Add confirmed check to the OrgHeaders request guard
2018-07-17 11:54:13 +02:00
Miroslav Prasil
de72655bb1 Add confirmed check to the OrgHeaders request guard 2018-07-16 10:23:45 +01:00
Daniel García
4a2350891a Merge pull request #84 from mqus/patch-2
Reflect changes in Archlinux packaging
2018-07-15 12:04:28 +02:00
mqus
4677ae4ac6 Reflect changes in Archlinux packaging
I changed the way bitwarden_rs is packaged (the web interface is now an addon-package instead of bundled) and added a 'stable' package which follows recent releases.
 I assume that following releases instead of the master branch is encouraged so I removed the link to the (still existing) bitwarden_rs-git package which does the latter.
2018-07-15 00:42:17 +02:00
Shane A. Faulkner
31349a47d3 Very dirty addition of missing api's 2018-07-14 01:09:20 -05:00
Daniel García
55b7a3e4d1 Merge pull request #82 from mprasil/not_accepted_user
Do not show organization stuff to not accepted user
2018-07-13 18:42:38 +02:00
Miroslav Prasil
692ed81306 Do not show organization stuff to not accepted user 2018-07-13 17:21:19 +01:00
13 changed files with 203 additions and 49 deletions

3
.env
View File

@@ -27,6 +27,9 @@
## The change only applies when the password is changed ## The change only applies when the password is changed
# PASSWORD_ITERATIONS=100000 # PASSWORD_ITERATIONS=100000
## Whether password hint should be sent into the error response when the client request it
# SHOW_PASSWORD_HINT=true
## Domain settings ## Domain settings
## The domain must match the address from where you access the server ## 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 ## Unless you are using U2F, or having problems with attachments not downloading, there is no need to change this

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "bitwarden_rs" name = "bitwarden_rs"
version = "0.10.0" version = "0.13.0"
authors = ["Daniel García <dani-garcia@users.noreply.github.com>"] authors = ["Daniel García <dani-garcia@users.noreply.github.com>"]
[dependencies] [dependencies]

View File

@@ -68,6 +68,7 @@ RUN cargo build --release
FROM debian:stretch-slim FROM debian:stretch-slim
ENV ROCKET_ENV "staging" ENV ROCKET_ENV "staging"
ENV ROCKET_WORKERS=10
# Install needed libraries # Install needed libraries
RUN apt-get update && apt-get install -y\ RUN apt-get update && apt-get install -y\

View File

@@ -4,8 +4,10 @@ Image is based on [Rust implementation of Bitwarden API](https://github.com/dani
_*Note, that this project is not associated with the [Bitwarden](https://bitwarden.com/) project nor 8bit Solutions LLC._ _*Note, that this project is not associated with the [Bitwarden](https://bitwarden.com/) project nor 8bit Solutions LLC._
## Table of contents <!-- omit in toc --> **Table of contents**
- [Features](#features) - [Features](#features)
- [Missing features](#missing-features)
- [Docker image usage](#docker-image-usage) - [Docker image usage](#docker-image-usage)
- [Starting a container](#starting-a-container) - [Starting a container](#starting-a-container)
- [Updating the bitwarden image](#updating-the-bitwarden-image) - [Updating the bitwarden image](#updating-the-bitwarden-image)
@@ -19,6 +21,8 @@ _*Note, that this project is not associated with the [Bitwarden](https://bitward
- [attachments location](#attachments-location) - [attachments location](#attachments-location)
- [icons cache](#icons-cache) - [icons cache](#icons-cache)
- [Changing the API request size limit](#changing-the-api-request-size-limit) - [Changing the API request size limit](#changing-the-api-request-size-limit)
- [Changing the number of workers](#changing-the-number-of-workers)
- [Disabling or overriding the Vault interface hosting](#disabling-or-overriding-the-vault-interface-hosting)
- [Other configuration](#other-configuration) - [Other configuration](#other-configuration)
- [Building your own image](#building-your-own-image) - [Building your own image](#building-your-own-image)
- [Building binary](#building-binary) - [Building binary](#building-binary)
@@ -30,6 +34,10 @@ _*Note, that this project is not associated with the [Bitwarden](https://bitward
- [3. the key files](#3-the-key-files) - [3. the key files](#3-the-key-files)
- [4. Icon Cache](#4-icon-cache) - [4. Icon Cache](#4-icon-cache)
- [Running the server with non-root user](#running-the-server-with-non-root-user) - [Running the server with non-root user](#running-the-server-with-non-root-user)
- [Differences from upstream API implementation](#differences-from-upstream-api-implementation)
- [Changing user email](#changing-user-email)
- [Creating organization](#creating-organization)
- [Inviting users into organization](#inviting-users-into-organization)
- [Get in touch](#get-in-touch) - [Get in touch](#get-in-touch)
## Features ## Features
@@ -131,11 +139,10 @@ Where:
```sh ```sh
docker run -d --name bitwarden \ docker run -d --name bitwarden \
-e ROCKET_TLS={certs='"/ssl/certs.pem",key="/ssl/key.pem"}' \ -e ROCKET_TLS='{certs="/ssl/certs.pem",key="/ssl/key.pem"}' \
-v /ssl/keys/:/ssl/ \ -v /ssl/keys/:/ssl/ \
-v /bw-data/:/data/ \ -v /bw-data/:/data/ \
-v /icon_cache/ \ -p 443:80 \
-p 443:443 \
mprasil/bitwarden:latest mprasil/bitwarden:latest
``` ```
Note that you need to mount ssl files and you need to forward appropriate port. Note that you need to mount ssl files and you need to forward appropriate port.
@@ -231,6 +238,44 @@ docker run -d --name bitwarden \
mprasil/bitwarden:latest mprasil/bitwarden:latest
``` ```
### Changing the number of workers
When you run bitwarden_rs, it spawns `2 * <number of cpu cores>` workers to handle requests. On some systems this might lead to low number of workers and hence slow performance, so the default in the docker image is changed to spawn 10 threads. You can override this setting to increase or decrease the number of workers by setting the `ROCKET_WORKERS` variable.
In the example bellow, we're starting with 20 workers:
```sh
docker run -d --name bitwarden \
-e ROCKET_WORKERS=20 \
-v /bw-data/:/data/ \
-p 80:80 \
mprasil/bitwarden:latest
```
### Disabling or overriding the Vault interface hosting
As a convenience bitwarden_rs image will also host static files for Vault web interface. You can disable this static file hosting completely by setting the WEB_VAULT_ENABLED variable.
```sh
docker run -d --name bitwarden \
-e WEB_VAULT_ENABLED=false \
-v /bw-data/:/data/ \
-p 80:80 \
mprasil/bitwarden:latest
```
Alternatively you can override the Vault files and provide your own static files to host. You can do that by mounting a path with your files over the `/web-vault` directory in the container. Just make sure the directory contains at least `index.html` file.
```sh
docker run -d --name bitwarden \
-v /path/to/static/files_directory:/web-vault \
-v /bw-data/:/data/ \
-p 80:80 \
mprasil/bitwarden:latest
```
Note that you can also change the path where bitwarden_rs looks for static files by providing the `WEB_VAULT_FOLDER` environment variable with the path.
### Other configuration ### Other configuration
Though this is unlikely to be required in small deployment, you can fine-tune some other settings like number of workers using environment variables that are processed by [Rocket](https://rocket.rs), please see details in [documentation](https://rocket.rs/guide/configuration/#environment-variables). Though this is unlikely to be required in small deployment, you can fine-tune some other settings like number of workers using environment variables that are processed by [Rocket](https://rocket.rs), please see details in [documentation](https://rocket.rs/guide/configuration/#environment-variables).
@@ -252,8 +297,7 @@ For building binary outside the Docker environment and running it locally withou
### Arch Linux ### Arch Linux
Bitwarden_rs is already packaged for Archlinux thanks to @mqus. There is an AUR package [with](https://aur.archlinux.org/packages/bitwarden_rs-vault-git/) and Bitwarden_rs is already packaged for Archlinux thanks to @mqus. There is an [AUR package](https://aur.archlinux.org/packages/bitwarden_rs) (optionally with the [vault web interface](https://aur.archlinux.org/packages/bitwarden_rs-vault/) ) available.
[without](https://aur.archlinux.org/packages/bitwarden_rs-git/) the vault web interface available.
## Backing up your vault ## Backing up your vault
@@ -297,8 +341,23 @@ docker run -d --name bitwarden \
-p 80:8080 \ -p 80:8080 \
mprasil/bitwarden:latest mprasil/bitwarden:latest
``` ```
## Differences from upstream API implementation
### Changing user email
Because we don't have any SMTP functionality at the moment, there's no way to deliver the verification token when you try to change the email. User just needs to enter any random token to continue and the change will be applied.
### Creating organization
We use upstream Vault interface directly without any (significant) changes, this is why user is presented with paid options when creating organization. To create an organization, just use the free option, none of the limits apply when using bitwarden_rs as back-end API and after the organization is created it should behave like Enterprise organization.
### Inviting users into organization
The users must already be registered on your server to invite them, because we can't send the invitation via email. The invited users won't get the invitation email, instead they will appear in the interface as if they already accepted the invitation. Organization admin then just needs to confirm them to be proper Organization members and to give them access to the shared secrets.
## Get in touch ## Get in touch
To ask an question, [raising an issue](https://github.com/dani-garcia/bitwarden_rs/issues/new) is fine, also please report any bugs spotted here. To ask an question, [raising an issue](https://github.com/dani-garcia/bitwarden_rs/issues/new) is fine, also please report any bugs spotted here.
If you prefer to chat, we're usually hanging around at [#bitwarden_rs:matrix.org](https://matrix.to/#/!cASGtOHlSftdScFNMs:matrix.org) room on Matrix. Feel free to join us! If you prefer to chat, we're usually hanging around at [#bitwarden_rs:matrix.org](https://matrix.to/#/#bitwarden_rs:matrix.org) room on Matrix. Feel free to join us!

View File

@@ -244,6 +244,29 @@ fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn
#[get("/accounts/revision-date")] #[get("/accounts/revision-date")]
fn revision_date(headers: Headers) -> String { fn revision_date(headers: Headers) -> String {
let revision_date = headers.user.updated_at.timestamp(); let revision_date = headers.user.updated_at.timestamp_millis();
revision_date.to_string() 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 !CONFIG.show_password_hint {
return Ok(())
}
match User::find_by_mail(&data.Email, &conn) {
Some(user) => {
let hint = user.password_hint.to_owned().unwrap_or_default();
err!(format!("Your password hint is: {}", hint))
},
None => Ok(()),
}
}

View File

@@ -415,6 +415,22 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
} }
#[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
fn post_attachment_admin(uuid: String, data: Data, content_type: &ContentType, headers: Headers, conn: DbConn) -> JsonResult {
post_attachment(uuid, data, content_type, headers, conn)
}
#[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
fn post_attachment_share(uuid: String, attachment_id: String, data: Data, content_type: &ContentType, headers: Headers, conn: DbConn) -> JsonResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn)?;
post_attachment(uuid, data, content_type, headers, conn)
}
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]
fn delete_attachment_post_admin(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
delete_attachment(uuid, attachment_id, headers, conn)
}
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")] #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
fn delete_attachment_post(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> EmptyResult { fn delete_attachment_post(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
delete_attachment(uuid, attachment_id, headers, conn) delete_attachment(uuid, attachment_id, headers, conn)
@@ -422,29 +438,7 @@ fn delete_attachment_post(uuid: String, attachment_id: String, headers: Headers,
#[delete("/ciphers/<uuid>/attachment/<attachment_id>")] #[delete("/ciphers/<uuid>/attachment/<attachment_id>")]
fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> EmptyResult { fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
let attachment = match Attachment::find_by_id(&attachment_id, &conn) { _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn)
Some(attachment) => attachment,
None => err!("Attachment doesn't exist")
};
if attachment.cipher_uuid != uuid {
err!("Attachment from other cipher")
}
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => cipher,
None => err!("Cipher doesn't exist")
};
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
err!("Cipher cannot be deleted by user")
}
// Delete attachment
match attachment.delete(&conn) {
Ok(()) => Ok(()),
Err(_) => err!("Deleting attachement failed")
}
} }
#[post("/ciphers/<uuid>/delete")] #[post("/ciphers/<uuid>/delete")]
@@ -578,3 +572,29 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn) -> Empty
Err(_) => err!("Failed deleting cipher") Err(_) => err!("Failed deleting cipher")
} }
} }
fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &Headers, conn: &DbConn) -> EmptyResult {
let attachment = match Attachment::find_by_id(&attachment_id, &conn) {
Some(attachment) => attachment,
None => err!("Attachment doesn't exist")
};
if attachment.cipher_uuid != uuid {
err!("Attachment from other cipher")
}
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => cipher,
None => err!("Cipher doesn't exist")
};
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
err!("Cipher cannot be deleted by user")
}
// Delete attachment
match attachment.delete(&conn) {
Ok(()) => Ok(()),
Err(_) => err!("Deleting attachement failed")
}
}

View File

@@ -23,6 +23,7 @@ pub fn routes() -> Vec<Route> {
post_email, post_email,
delete_account, delete_account,
revision_date, revision_date,
password_hint,
sync, sync,
@@ -34,7 +35,10 @@ pub fn routes() -> Vec<Route> {
post_ciphers_admin, post_ciphers_admin,
post_ciphers_import, post_ciphers_import,
post_attachment, post_attachment,
post_attachment_admin,
post_attachment_share,
delete_attachment_post, delete_attachment_post,
delete_attachment_post_admin,
delete_attachment, delete_attachment,
post_cipher_admin, post_cipher_admin,
post_cipher_share, post_cipher_share,

View File

@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
use rocket::request::Request; use rocket::request::Request;
use rocket::response::{self, NamedFile, Responder}; use rocket::response::{self, NamedFile, Responder};
use rocket::response::content::Content; use rocket::response::content::Content;
use rocket::http::ContentType; use rocket::http::{ContentType, Status};
use rocket::Route; use rocket::Route;
use rocket_contrib::{Json, Value}; use rocket_contrib::{Json, Value};
@@ -49,14 +49,19 @@ struct WebHeaders<R>(R);
impl<'r, R: Responder<'r>> Responder<'r> for WebHeaders<R> { impl<'r, R: Responder<'r>> Responder<'r> for WebHeaders<R> {
fn respond_to(self, req: &Request) -> response::Result<'r> { fn respond_to(self, req: &Request) -> response::Result<'r> {
let mut res = self.0.respond_to(req)?; match self.0.respond_to(req) {
Ok(mut res) => {
res.set_raw_header("Referrer-Policy", "same-origin");
res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
res.set_raw_header("X-Content-Type-Options", "nosniff");
res.set_raw_header("X-XSS-Protection", "1; mode=block");
res.set_raw_header("Referrer-Policy", "same-origin"); Ok(res)
res.set_raw_header("X-Frame-Options", "SAMEORIGIN"); },
res.set_raw_header("X-Content-Type-Options", "nosniff"); Err(_) => {
res.set_raw_header("X-XSS-Protection", "1; mode=block"); Err(Status::NotFound)
}
Ok(res) }
} }
} }

View File

@@ -95,7 +95,7 @@ use rocket::Outcome;
use rocket::request::{self, Request, FromRequest}; use rocket::request::{self, Request, FromRequest};
use db::DbConn; use db::DbConn;
use db::models::{User, UserOrganization, UserOrgType, Device}; use db::models::{User, UserOrganization, UserOrgType, UserOrgStatus, Device};
pub struct Headers { pub struct Headers {
pub host: String, pub host: String,
@@ -205,7 +205,13 @@ impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders {
}; };
let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
Some(user) => user, Some(user) => {
if user.status == UserOrgStatus::Confirmed as i32 {
user
} else {
err_handler!("The current user isn't confirmed member of the organization")
}
}
None => err_handler!("The current user isn't member of the organization") None => err_handler!("The current user isn't member of the organization")
}; };

View File

@@ -64,14 +64,33 @@ impl Attachment {
pub fn delete(self, conn: &DbConn) -> QueryResult<()> { pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
use util; use util;
use std::{thread, time};
let mut retries = 10;
loop {
match diesel::delete(
attachments::table.filter(
attachments::id.eq(&self.id)
)
).execute(&**conn) {
Ok(_) => break,
Err(err) => {
if retries < 1 {
println!("ERROR: Failed with 10 retries");
return Err(err)
} else {
retries = retries - 1;
println!("Had to retry! Retries left: {}", retries);
thread::sleep(time::Duration::from_millis(500));
continue
}
}
}
}
util::delete_file(&self.get_file_path()); util::delete_file(&self.get_file_path());
Ok(())
diesel::delete(
attachments::table.filter(
attachments::id.eq(self.id)
)
).execute(&**conn).and(Ok(()))
} }
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> { pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {

View File

@@ -2,7 +2,7 @@ use serde_json::Value as JsonValue;
use uuid::Uuid; use uuid::Uuid;
use super::{Organization, UserOrganization, UserOrgType}; use super::{Organization, UserOrganization, UserOrgType, UserOrgStatus};
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)] #[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
#[table_name = "collections"] #[table_name = "collections"]
@@ -78,13 +78,18 @@ impl Collection {
pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> { pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
let mut all_access_collections = users_organizations::table let mut all_access_collections = users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
.filter(users_organizations::access_all.eq(true)) .filter(users_organizations::access_all.eq(true))
.inner_join(collections::table.on(collections::org_uuid.eq(users_organizations::org_uuid))) .inner_join(collections::table.on(collections::org_uuid.eq(users_organizations::org_uuid)))
.select(collections::all_columns) .select(collections::all_columns)
.load::<Self>(&**conn).expect("Error loading collections"); .load::<Self>(&**conn).expect("Error loading collections");
let mut assigned_collections = users_collections::table.inner_join(collections::table) let mut assigned_collections = users_collections::table.inner_join(collections::table)
.left_join(users_organizations::table.on(
users_collections::user_uuid.eq(users_organizations::user_uuid)
))
.filter(users_collections::user_uuid.eq(user_uuid)) .filter(users_collections::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
.select(collections::all_columns) .select(collections::all_columns)
.load::<Self>(&**conn).expect("Error loading collections"); .load::<Self>(&**conn).expect("Error loading collections");

View File

@@ -268,6 +268,7 @@ impl UserOrganization {
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))
.load::<Self>(&**conn).unwrap_or(vec![]) .load::<Self>(&**conn).unwrap_or(vec![])
} }

View File

@@ -83,6 +83,11 @@ fn check_db() {
exit(1); exit(1);
} }
} }
// Turn on WAL in SQLite
use diesel::RunQueryDsl;
let connection = db::get_connection().expect("Can't conect to DB");
diesel::sql_query("PRAGMA journal_mode=wal").execute(&connection).expect("Failed to turn on WAL");
} }
fn check_rsa_keys() { fn check_rsa_keys() {
@@ -164,6 +169,7 @@ pub struct Config {
local_icon_extractor: bool, local_icon_extractor: bool,
signups_allowed: bool, signups_allowed: bool,
password_iterations: i32, password_iterations: i32,
show_password_hint: bool,
domain: String, domain: String,
domain_set: bool, domain_set: bool,
} }
@@ -192,6 +198,8 @@ impl Config {
local_icon_extractor: util::parse_option_string(env::var("LOCAL_ICON_EXTRACTOR").ok()).unwrap_or(false), local_icon_extractor: util::parse_option_string(env::var("LOCAL_ICON_EXTRACTOR").ok()).unwrap_or(false),
signups_allowed: util::parse_option_string(env::var("SIGNUPS_ALLOWED").ok()).unwrap_or(true), signups_allowed: util::parse_option_string(env::var("SIGNUPS_ALLOWED").ok()).unwrap_or(true),
password_iterations: util::parse_option_string(env::var("PASSWORD_ITERATIONS").ok()).unwrap_or(100_000), password_iterations: util::parse_option_string(env::var("PASSWORD_ITERATIONS").ok()).unwrap_or(100_000),
show_password_hint: util::parse_option_string(env::var("SHOW_PASSWORD_HINT").ok()).unwrap_or(true),
domain_set: domain.is_ok(), domain_set: domain.is_ok(),
domain: domain.unwrap_or("http://localhost".into()), domain: domain.unwrap_or("http://localhost".into()),
} }