geforkt von mirrored/vaultwarden
Commit
fd27759a95
18 geänderte Dateien mit 176 neuen und 167 gelöschten Zeilen
21
.github/workflows/build.yml
gevendort
21
.github/workflows/build.yml
gevendort
|
@ -10,6 +10,15 @@ on:
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
- "hooks/**"
|
- "hooks/**"
|
||||||
- "tools/**"
|
- "tools/**"
|
||||||
|
pull_request:
|
||||||
|
# Ignore when there are only changes done too one of these paths
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- "azure-pipelines.yml"
|
||||||
|
- "docker/**"
|
||||||
|
- "hooks/**"
|
||||||
|
- "tools/**"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -82,10 +91,11 @@ jobs:
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
target: ${{ matrix.target-triple }}
|
target: ${{ matrix.target-triple }}
|
||||||
|
components: clippy
|
||||||
# End Uses the rust-toolchain file to determine version
|
# End Uses the rust-toolchain file to determine version
|
||||||
|
|
||||||
|
|
||||||
# Run cargo tests (In release mode to speed up cargo build afterwards)
|
# Run cargo tests (In release mode to speed up future builds)
|
||||||
- name: '`cargo test --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}`'
|
- name: '`cargo test --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}`'
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
@ -94,6 +104,15 @@ jobs:
|
||||||
# End Run cargo tests
|
# End Run cargo tests
|
||||||
|
|
||||||
|
|
||||||
|
# Run cargo clippy (In release mode to speed up future builds)
|
||||||
|
- name: '`cargo clippy --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}`'
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}
|
||||||
|
# End Run cargo clippy
|
||||||
|
|
||||||
|
|
||||||
# Build the binary
|
# Build the binary
|
||||||
- name: '`cargo build --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}`'
|
- name: '`cargo build --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}`'
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rocket::{
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{ApiResult, EmptyResult, JsonResult, NumberOrString},
|
api::{ApiResult, EmptyResult, NumberOrString},
|
||||||
auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp},
|
auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp},
|
||||||
config::ConfigBuilder,
|
config::ConfigBuilder,
|
||||||
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
|
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
|
||||||
|
@ -312,17 +312,17 @@ fn test_smtp(data: Json<InviteData>, _token: AdminToken) -> EmptyResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/logout")]
|
#[get("/logout")]
|
||||||
fn logout(mut cookies: Cookies, referer: Referer) -> Result<Redirect, ()> {
|
fn logout(mut cookies: Cookies, referer: Referer) -> Redirect {
|
||||||
cookies.remove(Cookie::named(COOKIE_NAME));
|
cookies.remove(Cookie::named(COOKIE_NAME));
|
||||||
Ok(Redirect::to(admin_url(referer)))
|
Redirect::to(admin_url(referer))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users")]
|
#[get("/users")]
|
||||||
fn get_users_json(_token: AdminToken, conn: DbConn) -> JsonResult {
|
fn get_users_json(_token: AdminToken, conn: DbConn) -> Json<Value> {
|
||||||
let users = User::get_all(&conn);
|
let users = User::get_all(&conn);
|
||||||
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
||||||
|
|
||||||
Ok(Json(Value::Array(users_json)))
|
Json(Value::Array(users_json))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/overview")]
|
#[get("/users/overview")]
|
||||||
|
@ -564,7 +564,7 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
|
||||||
"running_within_docker": running_within_docker,
|
"running_within_docker": running_within_docker,
|
||||||
"has_http_access": has_http_access,
|
"has_http_access": has_http_access,
|
||||||
"ip_header_exists": &ip_header.0.is_some(),
|
"ip_header_exists": &ip_header.0.is_some(),
|
||||||
"ip_header_match": ip_header_name == &CONFIG.ip_header(),
|
"ip_header_match": ip_header_name == CONFIG.ip_header(),
|
||||||
"ip_header_name": ip_header_name,
|
"ip_header_name": ip_header_name,
|
||||||
"ip_header_config": &CONFIG.ip_header(),
|
"ip_header_config": &CONFIG.ip_header(),
|
||||||
"uses_proxy": uses_proxy,
|
"uses_proxy": uses_proxy,
|
||||||
|
@ -579,9 +579,9 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/diagnostics/config")]
|
#[get("/diagnostics/config")]
|
||||||
fn get_diagnostics_config(_token: AdminToken) -> JsonResult {
|
fn get_diagnostics_config(_token: AdminToken) -> Json<Value> {
|
||||||
let support_json = CONFIG.get_support_json();
|
let support_json = CONFIG.get_support_json();
|
||||||
Ok(Json(support_json))
|
Json(support_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/config", data = "<data>")]
|
#[post("/config", data = "<data>")]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType},
|
api::{EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType},
|
||||||
|
@ -139,10 +140,8 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
user.last_verifying_at = Some(user.created_at);
|
user.last_verifying_at = Some(user.created_at);
|
||||||
} else {
|
} else if let Err(e) = mail::send_welcome(&user.email) {
|
||||||
if let Err(e) = mail::send_welcome(&user.email) {
|
error!("Error sending welcome email: {:#?}", e);
|
||||||
error!("Error sending welcome email: {:#?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +149,8 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/accounts/profile")]
|
#[get("/accounts/profile")]
|
||||||
fn profile(headers: Headers, conn: DbConn) -> JsonResult {
|
fn profile(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
Ok(Json(headers.user.to_json(&conn)))
|
Json(headers.user.to_json(&conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -612,7 +611,7 @@ struct PreloginData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/prelogin", data = "<data>")]
|
#[post("/accounts/prelogin", data = "<data>")]
|
||||||
fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> JsonResult {
|
fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> Json<Value> {
|
||||||
let data: PreloginData = data.into_inner().data;
|
let data: PreloginData = data.into_inner().data;
|
||||||
|
|
||||||
let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &conn) {
|
let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &conn) {
|
||||||
|
@ -620,10 +619,10 @@ fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> JsonResult {
|
||||||
None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT),
|
None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Kdf": kdf_type,
|
"Kdf": kdf_type,
|
||||||
"KdfIterations": kdf_iter
|
"KdfIterations": kdf_iter
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|
|
@ -84,7 +84,7 @@ struct SyncData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/sync?<data..>")]
|
#[get("/sync?<data..>")]
|
||||||
fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
|
fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
let user_json = headers.user.to_json(&conn);
|
let user_json = headers.user.to_json(&conn);
|
||||||
|
|
||||||
let folders = Folder::find_by_user(&headers.user.uuid, &conn);
|
let folders = Folder::find_by_user(&headers.user.uuid, &conn);
|
||||||
|
@ -113,10 +113,10 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
let domains_json = if data.exclude_domains {
|
let domains_json = if data.exclude_domains {
|
||||||
Value::Null
|
Value::Null
|
||||||
} else {
|
} else {
|
||||||
api::core::_get_eq_domains(headers, true).unwrap().into_inner()
|
api::core::_get_eq_domains(headers, true).into_inner()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Profile": user_json,
|
"Profile": user_json,
|
||||||
"Folders": folders_json,
|
"Folders": folders_json,
|
||||||
"Collections": collections_json,
|
"Collections": collections_json,
|
||||||
|
@ -125,11 +125,11 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
"Domains": domains_json,
|
"Domains": domains_json,
|
||||||
"Sends": sends_json,
|
"Sends": sends_json,
|
||||||
"Object": "sync"
|
"Object": "sync"
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/ciphers")]
|
#[get("/ciphers")]
|
||||||
fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
|
let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
|
||||||
|
|
||||||
let ciphers_json: Vec<Value> = ciphers
|
let ciphers_json: Vec<Value> = ciphers
|
||||||
|
@ -137,11 +137,11 @@ fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data": ciphers_json,
|
"Data": ciphers_json,
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null
|
"ContinuationToken": null
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/ciphers/<uuid>")]
|
#[get("/ciphers/<uuid>")]
|
||||||
|
|
|
@ -20,16 +20,16 @@ pub fn routes() -> Vec<rocket::Route> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/folders")]
|
#[get("/folders")]
|
||||||
fn get_folders(headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_folders(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
let folders = Folder::find_by_user(&headers.user.uuid, &conn);
|
let folders = Folder::find_by_user(&headers.user.uuid, &conn);
|
||||||
|
|
||||||
let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
|
let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data": folders_json,
|
"Data": folders_json,
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null,
|
"ContinuationToken": null,
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/folders/<uuid>")]
|
#[get("/folders/<uuid>")]
|
||||||
|
|
|
@ -34,17 +34,18 @@ pub fn routes() -> Vec<Route> {
|
||||||
//
|
//
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
use rocket::response::Response;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{EmptyResult, JsonResult, JsonUpcase},
|
api::{JsonResult, JsonUpcase},
|
||||||
auth::Headers,
|
auth::Headers,
|
||||||
db::DbConn,
|
db::DbConn,
|
||||||
error::Error,
|
error::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[put("/devices/identifier/<uuid>/clear-token")]
|
#[put("/devices/identifier/<uuid>/clear-token")]
|
||||||
fn clear_device_token(uuid: String) -> EmptyResult {
|
fn clear_device_token<'a>(uuid: String) -> Response<'a> {
|
||||||
// This endpoint doesn't have auth header
|
// This endpoint doesn't have auth header
|
||||||
|
|
||||||
let _ = uuid;
|
let _ = uuid;
|
||||||
|
@ -53,11 +54,11 @@ fn clear_device_token(uuid: String) -> EmptyResult {
|
||||||
// This only clears push token
|
// This only clears push token
|
||||||
// https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109
|
// https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109
|
||||||
// https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37
|
// https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37
|
||||||
Ok(())
|
Response::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/devices/identifier/<uuid>/token", data = "<data>")]
|
#[put("/devices/identifier/<uuid>/token", data = "<data>")]
|
||||||
fn put_device_token(uuid: String, data: JsonUpcase<Value>, headers: Headers) -> JsonResult {
|
fn put_device_token(uuid: String, data: JsonUpcase<Value>, headers: Headers) -> Json<Value> {
|
||||||
let _data: Value = data.into_inner().data;
|
let _data: Value = data.into_inner().data;
|
||||||
// Data has a single string value "PushToken"
|
// Data has a single string value "PushToken"
|
||||||
let _ = uuid;
|
let _ = uuid;
|
||||||
|
@ -65,13 +66,13 @@ fn put_device_token(uuid: String, data: JsonUpcase<Value>, headers: Headers) ->
|
||||||
|
|
||||||
// TODO: This should save the push token, but we don't have push functionality
|
// TODO: This should save the push token, but we don't have push functionality
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Id": headers.device.uuid,
|
"Id": headers.device.uuid,
|
||||||
"Name": headers.device.name,
|
"Name": headers.device.name,
|
||||||
"Type": headers.device.atype,
|
"Type": headers.device.atype,
|
||||||
"Identifier": headers.device.uuid,
|
"Identifier": headers.device.uuid,
|
||||||
"CreationDate": crate::util::format_date(&headers.device.created_at),
|
"CreationDate": crate::util::format_date(&headers.device.created_at),
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -85,11 +86,11 @@ struct GlobalDomain {
|
||||||
const GLOBAL_DOMAINS: &str = include_str!("../../static/global_domains.json");
|
const GLOBAL_DOMAINS: &str = include_str!("../../static/global_domains.json");
|
||||||
|
|
||||||
#[get("/settings/domains")]
|
#[get("/settings/domains")]
|
||||||
fn get_eq_domains(headers: Headers) -> JsonResult {
|
fn get_eq_domains(headers: Headers) -> Json<Value> {
|
||||||
_get_eq_domains(headers, false)
|
_get_eq_domains(headers, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _get_eq_domains(headers: Headers, no_excluded: bool) -> JsonResult {
|
fn _get_eq_domains(headers: Headers, no_excluded: bool) -> Json<Value> {
|
||||||
let user = headers.user;
|
let user = headers.user;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
|
@ -106,11 +107,11 @@ fn _get_eq_domains(headers: Headers, no_excluded: bool) -> JsonResult {
|
||||||
globals.retain(|g| !g.Excluded);
|
globals.retain(|g| !g.Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"EquivalentDomains": equivalent_domains,
|
"EquivalentDomains": equivalent_domains,
|
||||||
"GlobalEquivalentDomains": globals,
|
"GlobalEquivalentDomains": globals,
|
||||||
"Object": "domains",
|
"Object": "domains",
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
|
|
@ -192,8 +192,8 @@ fn post_organization(
|
||||||
|
|
||||||
// GET /api/collections?writeOnly=false
|
// GET /api/collections?writeOnly=false
|
||||||
#[get("/collections")]
|
#[get("/collections")]
|
||||||
fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_user_collections(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data":
|
"Data":
|
||||||
Collection::find_by_user_uuid(&headers.user.uuid, &conn)
|
Collection::find_by_user_uuid(&headers.user.uuid, &conn)
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -201,12 +201,12 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
.collect::<Value>(),
|
.collect::<Value>(),
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null,
|
"ContinuationToken": null,
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<org_id>/collections")]
|
#[get("/organizations/<org_id>/collections")]
|
||||||
fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
|
fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) -> Json<Value> {
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data":
|
"Data":
|
||||||
Collection::find_by_organization(&org_id, &conn)
|
Collection::find_by_organization(&org_id, &conn)
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -214,7 +214,7 @@ fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) ->
|
||||||
.collect::<Value>(),
|
.collect::<Value>(),
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null,
|
"ContinuationToken": null,
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/organizations/<org_id>/collections", data = "<data>")]
|
#[post("/organizations/<org_id>/collections", data = "<data>")]
|
||||||
|
@ -441,30 +441,30 @@ struct OrgIdData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/ciphers/organization-details?<data..>")]
|
#[get("/ciphers/organization-details?<data..>")]
|
||||||
fn get_org_details(data: Form<OrgIdData>, headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_org_details(data: Form<OrgIdData>, headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
|
let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
|
||||||
let ciphers_json: Vec<Value> = ciphers
|
let ciphers_json: Vec<Value> = ciphers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data": ciphers_json,
|
"Data": ciphers_json,
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null,
|
"ContinuationToken": null,
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<org_id>/users")]
|
#[get("/organizations/<org_id>/users")]
|
||||||
fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> JsonResult {
|
fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json<Value> {
|
||||||
let users = UserOrganization::find_by_org(&org_id, &conn);
|
let users = UserOrganization::find_by_org(&org_id, &conn);
|
||||||
let users_json: Vec<Value> = users.iter().map(|c| c.to_json_user_details(&conn)).collect();
|
let users_json: Vec<Value> = users.iter().map(|c| c.to_json_user_details(&conn)).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data": users_json,
|
"Data": users_json,
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null,
|
"ContinuationToken": null,
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -930,15 +930,15 @@ fn post_org_import(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<org_id>/policies")]
|
#[get("/organizations/<org_id>/policies")]
|
||||||
fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
|
fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> Json<Value> {
|
||||||
let policies = OrgPolicy::find_by_org(&org_id, &conn);
|
let policies = OrgPolicy::find_by_org(&org_id, &conn);
|
||||||
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
|
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data": policies_json,
|
"Data": policies_json,
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null
|
"ContinuationToken": null
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<org_id>/policies/token?<token>")]
|
#[get("/organizations/<org_id>/policies/token?<token>")]
|
||||||
|
@ -1017,8 +1017,8 @@ fn get_organization_tax(org_id: String, _headers: Headers, _conn: DbConn) -> Emp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/plans")]
|
#[get("/plans")]
|
||||||
fn get_plans(_headers: Headers, _conn: DbConn) -> JsonResult {
|
fn get_plans(_headers: Headers, _conn: DbConn) -> Json<Value> {
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"Data": [
|
"Data": [
|
||||||
{
|
{
|
||||||
|
@ -1065,17 +1065,17 @@ fn get_plans(_headers: Headers, _conn: DbConn) -> JsonResult {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ContinuationToken": null
|
"ContinuationToken": null
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/plans/sales-tax-rates")]
|
#[get("/plans/sales-tax-rates")]
|
||||||
fn get_plans_tax_rates(_headers: Headers, _conn: DbConn) -> JsonResult {
|
fn get_plans_tax_rates(_headers: Headers, _conn: DbConn) -> Json<Value> {
|
||||||
// Prevent a 404 error, which also causes Javascript errors.
|
// Prevent a 404 error, which also causes Javascript errors.
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"Data": [],
|
"Data": [],
|
||||||
"ContinuationToken": null
|
"ContinuationToken": null
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -1128,7 +1128,7 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
|
||||||
// If user is not part of the organization, but it exists
|
// If user is not part of the organization, but it exists
|
||||||
} else if UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).is_none() {
|
} else if UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).is_none() {
|
||||||
if let Some (user) = User::find_by_mail(&user_data.Email, &conn) {
|
if let Some (user) = User::find_by_mail(&user_data.Email, &conn) {
|
||||||
|
|
||||||
let user_org_status = if CONFIG.mail_enabled() {
|
let user_org_status = if CONFIG.mail_enabled() {
|
||||||
UserOrgStatus::Invited as i32
|
UserOrgStatus::Invited as i32
|
||||||
} else {
|
} else {
|
||||||
|
@ -1157,18 +1157,18 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
|
||||||
Some(headers.user.email.clone()),
|
Some(headers.user.email.clone()),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true)
|
// If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true)
|
||||||
if data.OverwriteExisting {
|
if data.OverwriteExisting {
|
||||||
for user_org in UserOrganization::find_by_org_and_type(&org_id, UserOrgType::User as i32, &conn) {
|
for user_org in UserOrganization::find_by_org_and_type(&org_id, UserOrgType::User as i32, &conn) {
|
||||||
if let Some (user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).map(|u| u.email) {
|
if let Some (user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).map(|u| u.email) {
|
||||||
if !data.Users.iter().any(|u| u.Email == user_email) {
|
if !data.Users.iter().any(|u| u.Email == user_email) {
|
||||||
user_org.delete(&conn)?;
|
user_org.delete(&conn)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,15 +38,15 @@ pub fn routes() -> Vec<Route> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/two-factor")]
|
#[get("/two-factor")]
|
||||||
fn get_twofactor(headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_twofactor(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||||
let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn);
|
let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn);
|
||||||
let twofactors_json: Vec<Value> = twofactors.iter().map(TwoFactor::to_json_provider).collect();
|
let twofactors_json: Vec<Value> = twofactors.iter().map(TwoFactor::to_json_provider).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"Data": twofactors_json,
|
"Data": twofactors_json,
|
||||||
"Object": "list",
|
"Object": "list",
|
||||||
"ContinuationToken": null,
|
"ContinuationToken": null,
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/two-factor/get-recover", data = "<data>")]
|
#[post("/two-factor/get-recover", data = "<data>")]
|
||||||
|
|
|
@ -131,12 +131,12 @@ struct RegisterResponseCopy {
|
||||||
pub error_code: Option<NumberOrString>,
|
pub error_code: Option<NumberOrString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<RegisterResponse> for RegisterResponseCopy {
|
impl From<RegisterResponseCopy> for RegisterResponse {
|
||||||
fn into(self) -> RegisterResponse {
|
fn from(r: RegisterResponseCopy) -> RegisterResponse {
|
||||||
RegisterResponse {
|
RegisterResponse {
|
||||||
registration_data: self.registration_data,
|
registration_data: r.registration_data,
|
||||||
version: self.version,
|
version: r.version,
|
||||||
client_data: self.client_data,
|
client_data: r.client_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,10 +352,12 @@ fn get_favicons_node(node: &std::rc::Rc<markup5ever_rcdom::Node>, icons: &mut Ve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_rel && href.is_some() {
|
if has_rel {
|
||||||
if let Ok(full_href) = url.join(&href.unwrap()).map(|h| h.into_string()) {
|
if let Some(inner_href) = href {
|
||||||
let priority = get_icon_priority(&full_href, sizes);
|
if let Ok(full_href) = url.join(&inner_href).map(|h| h.into_string()) {
|
||||||
icons.push(Icon::new(priority, full_href));
|
let priority = get_icon_priority(&full_href, sizes);
|
||||||
|
icons.push(Icon::new(priority, full_href));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rocket_contrib::json::Json;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{EmptyResult, JsonResult},
|
api::EmptyResult,
|
||||||
auth::Headers,
|
auth::Headers,
|
||||||
db::DbConn,
|
db::DbConn,
|
||||||
Error, CONFIG,
|
Error, CONFIG,
|
||||||
|
@ -31,7 +31,7 @@ fn websockets_err() -> EmptyResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/hub/negotiate")]
|
#[post("/hub/negotiate")]
|
||||||
fn negotiate(_headers: Headers, _conn: DbConn) -> JsonResult {
|
fn negotiate(_headers: Headers, _conn: DbConn) -> Json<JsonValue> {
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
use data_encoding::BASE64URL;
|
use data_encoding::BASE64URL;
|
||||||
|
|
||||||
|
@ -47,10 +47,10 @@ fn negotiate(_headers: Headers, _conn: DbConn) -> JsonResult {
|
||||||
// Rocket SSE support: https://github.com/SergioBenitez/Rocket/issues/33
|
// Rocket SSE support: https://github.com/SergioBenitez/Rocket/issues/33
|
||||||
// {"transport":"ServerSentEvents", "transferFormats":["Text"]},
|
// {"transport":"ServerSentEvents", "transferFormats":["Text"]},
|
||||||
// {"transport":"LongPolling", "transferFormats":["Text","Binary"]}
|
// {"transport":"LongPolling", "transferFormats":["Text","Binary"]}
|
||||||
Ok(Json(json!({
|
Json(json!({
|
||||||
"connectionId": conn_id,
|
"connectionId": conn_id,
|
||||||
"availableTransports": available_transports
|
"availableTransports": available_transports
|
||||||
})))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -120,7 +120,7 @@ fn convert_option<T: Into<Value>>(option: Option<T>) -> Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server WebSocket handler
|
// Server WebSocket handler
|
||||||
pub struct WSHandler {
|
pub struct WsHandler {
|
||||||
out: Sender,
|
out: Sender,
|
||||||
user_uuid: Option<String>,
|
user_uuid: Option<String>,
|
||||||
users: WebSocketUsers,
|
users: WebSocketUsers,
|
||||||
|
@ -140,7 +140,7 @@ const PING: Token = Token(1);
|
||||||
|
|
||||||
const ACCESS_TOKEN_KEY: &str = "access_token=";
|
const ACCESS_TOKEN_KEY: &str = "access_token=";
|
||||||
|
|
||||||
impl WSHandler {
|
impl WsHandler {
|
||||||
fn err(&self, msg: &'static str) -> ws::Result<()> {
|
fn err(&self, msg: &'static str) -> ws::Result<()> {
|
||||||
self.out.close(ws::CloseCode::Invalid)?;
|
self.out.close(ws::CloseCode::Invalid)?;
|
||||||
|
|
||||||
|
@ -166,8 +166,8 @@ impl WSHandler {
|
||||||
if let Some(params) = path.split('?').nth(1) {
|
if let Some(params) = path.split('?').nth(1) {
|
||||||
let params_iter = params.split('&').take(1);
|
let params_iter = params.split('&').take(1);
|
||||||
for val in params_iter {
|
for val in params_iter {
|
||||||
if val.starts_with(ACCESS_TOKEN_KEY) {
|
if let Some(stripped) = val.strip_prefix(ACCESS_TOKEN_KEY) {
|
||||||
return Some(val[ACCESS_TOKEN_KEY.len()..].into());
|
return Some(stripped.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -176,7 +176,7 @@ impl WSHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for WSHandler {
|
impl Handler for WsHandler {
|
||||||
fn on_open(&mut self, hs: Handshake) -> ws::Result<()> {
|
fn on_open(&mut self, hs: Handshake) -> ws::Result<()> {
|
||||||
// Path == "/notifications/hub?id=<id>==&access_token=<access_token>"
|
// Path == "/notifications/hub?id=<id>==&access_token=<access_token>"
|
||||||
//
|
//
|
||||||
|
@ -240,13 +240,13 @@ impl Handler for WSHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WSFactory {
|
struct WsFactory {
|
||||||
pub users: WebSocketUsers,
|
pub users: WebSocketUsers,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WSFactory {
|
impl WsFactory {
|
||||||
pub fn init() -> Self {
|
pub fn init() -> Self {
|
||||||
WSFactory {
|
WsFactory {
|
||||||
users: WebSocketUsers {
|
users: WebSocketUsers {
|
||||||
map: Arc::new(CHashMap::new()),
|
map: Arc::new(CHashMap::new()),
|
||||||
},
|
},
|
||||||
|
@ -254,11 +254,11 @@ impl WSFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Factory for WSFactory {
|
impl Factory for WsFactory {
|
||||||
type Handler = WSHandler;
|
type Handler = WsHandler;
|
||||||
|
|
||||||
fn connection_made(&mut self, out: Sender) -> Self::Handler {
|
fn connection_made(&mut self, out: Sender) -> Self::Handler {
|
||||||
WSHandler {
|
WsHandler {
|
||||||
out,
|
out,
|
||||||
user_uuid: None,
|
user_uuid: None,
|
||||||
users: self.users.clone(),
|
users: self.users.clone(),
|
||||||
|
@ -405,15 +405,17 @@ use rocket::State;
|
||||||
pub type Notify<'a> = State<'a, WebSocketUsers>;
|
pub type Notify<'a> = State<'a, WebSocketUsers>;
|
||||||
|
|
||||||
pub fn start_notification_server() -> WebSocketUsers {
|
pub fn start_notification_server() -> WebSocketUsers {
|
||||||
let factory = WSFactory::init();
|
let factory = WsFactory::init();
|
||||||
let users = factory.users.clone();
|
let users = factory.users.clone();
|
||||||
|
|
||||||
if CONFIG.websocket_enabled() {
|
if CONFIG.websocket_enabled() {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut settings = ws::Settings::default();
|
let settings = ws::Settings {
|
||||||
settings.max_connections = 500;
|
max_connections: 500,
|
||||||
settings.queue_size = 2;
|
queue_size: 2,
|
||||||
settings.panic_on_internal = false;
|
panic_on_internal: false,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
ws::Builder::new()
|
ws::Builder::new()
|
||||||
.with_settings(settings)
|
.with_settings(settings)
|
||||||
|
|
66
src/auth.rs
66
src/auth.rs
|
@ -58,28 +58,28 @@ fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Err
|
||||||
.map_res("Error decoding JWT")
|
.map_res("Error decoding JWT")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_login(token: &str) -> Result<LoginJWTClaims, Error> {
|
pub fn decode_login(token: &str) -> Result<LoginJwtClaims, Error> {
|
||||||
decode_jwt(token, JWT_LOGIN_ISSUER.to_string())
|
decode_jwt(token, JWT_LOGIN_ISSUER.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_invite(token: &str) -> Result<InviteJWTClaims, Error> {
|
pub fn decode_invite(token: &str) -> Result<InviteJwtClaims, Error> {
|
||||||
decode_jwt(token, JWT_INVITE_ISSUER.to_string())
|
decode_jwt(token, JWT_INVITE_ISSUER.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_delete(token: &str) -> Result<DeleteJWTClaims, Error> {
|
pub fn decode_delete(token: &str) -> Result<DeleteJwtClaims, Error> {
|
||||||
decode_jwt(token, JWT_DELETE_ISSUER.to_string())
|
decode_jwt(token, JWT_DELETE_ISSUER.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_verify_email(token: &str) -> Result<VerifyEmailJWTClaims, Error> {
|
pub fn decode_verify_email(token: &str) -> Result<VerifyEmailJwtClaims, Error> {
|
||||||
decode_jwt(token, JWT_VERIFYEMAIL_ISSUER.to_string())
|
decode_jwt(token, JWT_VERIFYEMAIL_ISSUER.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_admin(token: &str) -> Result<AdminJWTClaims, Error> {
|
pub fn decode_admin(token: &str) -> Result<AdminJwtClaims, Error> {
|
||||||
decode_jwt(token, JWT_ADMIN_ISSUER.to_string())
|
decode_jwt(token, JWT_ADMIN_ISSUER.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct LoginJWTClaims {
|
pub struct LoginJwtClaims {
|
||||||
// Not before
|
// Not before
|
||||||
pub nbf: i64,
|
pub nbf: i64,
|
||||||
// Expiration time
|
// Expiration time
|
||||||
|
@ -110,7 +110,7 @@ pub struct LoginJWTClaims {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct InviteJWTClaims {
|
pub struct InviteJwtClaims {
|
||||||
// Not before
|
// Not before
|
||||||
pub nbf: i64,
|
pub nbf: i64,
|
||||||
// Expiration time
|
// Expiration time
|
||||||
|
@ -132,9 +132,9 @@ pub fn generate_invite_claims(
|
||||||
org_id: Option<String>,
|
org_id: Option<String>,
|
||||||
user_org_id: Option<String>,
|
user_org_id: Option<String>,
|
||||||
invited_by_email: Option<String>,
|
invited_by_email: Option<String>,
|
||||||
) -> InviteJWTClaims {
|
) -> InviteJwtClaims {
|
||||||
let time_now = Utc::now().naive_utc();
|
let time_now = Utc::now().naive_utc();
|
||||||
InviteJWTClaims {
|
InviteJwtClaims {
|
||||||
nbf: time_now.timestamp(),
|
nbf: time_now.timestamp(),
|
||||||
exp: (time_now + Duration::days(5)).timestamp(),
|
exp: (time_now + Duration::days(5)).timestamp(),
|
||||||
iss: JWT_INVITE_ISSUER.to_string(),
|
iss: JWT_INVITE_ISSUER.to_string(),
|
||||||
|
@ -147,7 +147,7 @@ pub fn generate_invite_claims(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct DeleteJWTClaims {
|
pub struct DeleteJwtClaims {
|
||||||
// Not before
|
// Not before
|
||||||
pub nbf: i64,
|
pub nbf: i64,
|
||||||
// Expiration time
|
// Expiration time
|
||||||
|
@ -158,9 +158,9 @@ pub struct DeleteJWTClaims {
|
||||||
pub sub: String,
|
pub sub: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_delete_claims(uuid: String) -> DeleteJWTClaims {
|
pub fn generate_delete_claims(uuid: String) -> DeleteJwtClaims {
|
||||||
let time_now = Utc::now().naive_utc();
|
let time_now = Utc::now().naive_utc();
|
||||||
DeleteJWTClaims {
|
DeleteJwtClaims {
|
||||||
nbf: time_now.timestamp(),
|
nbf: time_now.timestamp(),
|
||||||
exp: (time_now + Duration::days(5)).timestamp(),
|
exp: (time_now + Duration::days(5)).timestamp(),
|
||||||
iss: JWT_DELETE_ISSUER.to_string(),
|
iss: JWT_DELETE_ISSUER.to_string(),
|
||||||
|
@ -169,7 +169,7 @@ pub fn generate_delete_claims(uuid: String) -> DeleteJWTClaims {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct VerifyEmailJWTClaims {
|
pub struct VerifyEmailJwtClaims {
|
||||||
// Not before
|
// Not before
|
||||||
pub nbf: i64,
|
pub nbf: i64,
|
||||||
// Expiration time
|
// Expiration time
|
||||||
|
@ -180,9 +180,9 @@ pub struct VerifyEmailJWTClaims {
|
||||||
pub sub: String,
|
pub sub: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_verify_email_claims(uuid: String) -> DeleteJWTClaims {
|
pub fn generate_verify_email_claims(uuid: String) -> DeleteJwtClaims {
|
||||||
let time_now = Utc::now().naive_utc();
|
let time_now = Utc::now().naive_utc();
|
||||||
DeleteJWTClaims {
|
DeleteJwtClaims {
|
||||||
nbf: time_now.timestamp(),
|
nbf: time_now.timestamp(),
|
||||||
exp: (time_now + Duration::days(5)).timestamp(),
|
exp: (time_now + Duration::days(5)).timestamp(),
|
||||||
iss: JWT_VERIFYEMAIL_ISSUER.to_string(),
|
iss: JWT_VERIFYEMAIL_ISSUER.to_string(),
|
||||||
|
@ -191,7 +191,7 @@ pub fn generate_verify_email_claims(uuid: String) -> DeleteJWTClaims {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct AdminJWTClaims {
|
pub struct AdminJwtClaims {
|
||||||
// Not before
|
// Not before
|
||||||
pub nbf: i64,
|
pub nbf: i64,
|
||||||
// Expiration time
|
// Expiration time
|
||||||
|
@ -202,9 +202,9 @@ pub struct AdminJWTClaims {
|
||||||
pub sub: String,
|
pub sub: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_admin_claims() -> AdminJWTClaims {
|
pub fn generate_admin_claims() -> AdminJwtClaims {
|
||||||
let time_now = Utc::now().naive_utc();
|
let time_now = Utc::now().naive_utc();
|
||||||
AdminJWTClaims {
|
AdminJwtClaims {
|
||||||
nbf: time_now.timestamp(),
|
nbf: time_now.timestamp(),
|
||||||
exp: (time_now + Duration::minutes(20)).timestamp(),
|
exp: (time_now + Duration::minutes(20)).timestamp(),
|
||||||
iss: JWT_ADMIN_ISSUER.to_string(),
|
iss: JWT_ADMIN_ISSUER.to_string(),
|
||||||
|
@ -450,12 +450,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Headers> for AdminHeaders {
|
impl From<AdminHeaders> for Headers {
|
||||||
fn into(self) -> Headers {
|
fn from(h: AdminHeaders) -> Headers {
|
||||||
Headers {
|
Headers {
|
||||||
host: self.host,
|
host: h.host,
|
||||||
device: self.device,
|
device: h.device,
|
||||||
user: self.user,
|
user: h.user,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,12 +529,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeaders {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Headers> for ManagerHeaders {
|
impl From<ManagerHeaders> for Headers {
|
||||||
fn into(self) -> Headers {
|
fn from(h: ManagerHeaders) -> Headers {
|
||||||
Headers {
|
Headers {
|
||||||
host: self.host,
|
host: h.host,
|
||||||
device: self.device,
|
device: h.device,
|
||||||
user: self.user,
|
user: h.user,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -571,12 +571,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeadersLoose {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Headers> for ManagerHeadersLoose {
|
impl From<ManagerHeadersLoose> for Headers {
|
||||||
fn into(self) -> Headers {
|
fn from(h: ManagerHeadersLoose) -> Headers {
|
||||||
Headers {
|
Headers {
|
||||||
host: self.host,
|
host: h.host,
|
||||||
device: self.device,
|
device: h.device,
|
||||||
user: self.user,
|
user: h.user,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl Cipher {
|
||||||
|
|
||||||
// Get the type_data or a default to an empty json object '{}'.
|
// Get the type_data or a default to an empty json object '{}'.
|
||||||
// If not passing an empty object, mobile clients will crash.
|
// If not passing an empty object, mobile clients will crash.
|
||||||
let mut type_data_json: Value = serde_json::from_str(&self.data).unwrap_or(json!({}));
|
let mut type_data_json: Value = serde_json::from_str(&self.data).unwrap_or_else(|_| json!({}));
|
||||||
|
|
||||||
// NOTE: This was marked as *Backwards Compatibilty Code*, but as of January 2021 this is still being used by upstream
|
// NOTE: This was marked as *Backwards Compatibilty Code*, but as of January 2021 this is still being used by upstream
|
||||||
// Set the first element of the Uris array as Uri, this is needed several (mobile) clients.
|
// Set the first element of the Uris array as Uri, this is needed several (mobile) clients.
|
||||||
|
|
|
@ -80,8 +80,8 @@ impl Device {
|
||||||
let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect();
|
let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect();
|
||||||
|
|
||||||
// Create the JWT claims struct, to send to the client
|
// Create the JWT claims struct, to send to the client
|
||||||
use crate::auth::{encode_jwt, LoginJWTClaims, DEFAULT_VALIDITY, JWT_LOGIN_ISSUER};
|
use crate::auth::{encode_jwt, LoginJwtClaims, DEFAULT_VALIDITY, JWT_LOGIN_ISSUER};
|
||||||
let claims = LoginJWTClaims {
|
let claims = LoginJwtClaims {
|
||||||
nbf: time_now.timestamp(),
|
nbf: time_now.timestamp(),
|
||||||
exp: (time_now + *DEFAULT_VALIDITY).timestamp(),
|
exp: (time_now + *DEFAULT_VALIDITY).timestamp(),
|
||||||
iss: JWT_LOGIN_ISSUER.to_string(),
|
iss: JWT_LOGIN_ISSUER.to_string(),
|
||||||
|
@ -117,7 +117,7 @@ impl Device {
|
||||||
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
self.updated_at = Utc::now().naive_utc();
|
self.updated_at = Utc::now().naive_utc();
|
||||||
|
|
||||||
db_run! { conn:
|
db_run! { conn:
|
||||||
sqlite, mysql {
|
sqlite, mysql {
|
||||||
crate::util::retry(
|
crate::util::retry(
|
||||||
|| diesel::replace_into(devices::table).values(DeviceDb::to_db(self)).execute(conn),
|
|| diesel::replace_into(devices::table).values(DeviceDb::to_db(self)).execute(conn),
|
||||||
|
|
|
@ -90,17 +90,11 @@ impl PartialOrd<i32> for UserOrgType {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gt(&self, other: &i32) -> bool {
|
fn gt(&self, other: &i32) -> bool {
|
||||||
match self.partial_cmp(other) {
|
matches!(self.partial_cmp(other), Some(Ordering::Greater))
|
||||||
Some(Ordering::Less) | Some(Ordering::Equal) => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ge(&self, other: &i32) -> bool {
|
fn ge(&self, other: &i32) -> bool {
|
||||||
match self.partial_cmp(other) {
|
matches!(self.partial_cmp(other), Some(Ordering::Greater) | Some(Ordering::Equal))
|
||||||
Some(Ordering::Less) => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,17 +113,11 @@ impl PartialOrd<UserOrgType> for i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lt(&self, other: &UserOrgType) -> bool {
|
fn lt(&self, other: &UserOrgType) -> bool {
|
||||||
match self.partial_cmp(other) {
|
matches!(self.partial_cmp(other), Some(Ordering::Less) | None)
|
||||||
Some(Ordering::Less) | None => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn le(&self, other: &UserOrgType) -> bool {
|
fn le(&self, other: &UserOrgType) -> bool {
|
||||||
match self.partial_cmp(other) {
|
matches!(self.partial_cmp(other), Some(Ordering::Less) | Some(Ordering::Equal) | None)
|
||||||
Some(Ordering::Less) | Some(Ordering::Equal) | None => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,11 @@ use diesel::ConnectionError as DieselConErr;
|
||||||
use diesel_migrations::RunMigrationsError as DieselMigErr;
|
use diesel_migrations::RunMigrationsError as DieselMigErr;
|
||||||
use diesel::r2d2::PoolError as R2d2Err;
|
use diesel::r2d2::PoolError as R2d2Err;
|
||||||
use handlebars::RenderError as HbErr;
|
use handlebars::RenderError as HbErr;
|
||||||
use jsonwebtoken::errors::Error as JWTErr;
|
use jsonwebtoken::errors::Error as JwtErr;
|
||||||
use regex::Error as RegexErr;
|
use regex::Error as RegexErr;
|
||||||
use reqwest::Error as ReqErr;
|
use reqwest::Error as ReqErr;
|
||||||
use serde_json::{Error as SerdeErr, Value};
|
use serde_json::{Error as SerdeErr, Value};
|
||||||
use std::io::Error as IOErr;
|
use std::io::Error as IoErr;
|
||||||
|
|
||||||
use std::time::SystemTimeError as TimeErr;
|
use std::time::SystemTimeError as TimeErr;
|
||||||
use u2f::u2ferror::U2fError as U2fErr;
|
use u2f::u2ferror::U2fError as U2fErr;
|
||||||
|
@ -72,10 +72,10 @@ make_error! {
|
||||||
R2d2Error(R2d2Err): _has_source, _api_error,
|
R2d2Error(R2d2Err): _has_source, _api_error,
|
||||||
U2fError(U2fErr): _has_source, _api_error,
|
U2fError(U2fErr): _has_source, _api_error,
|
||||||
SerdeError(SerdeErr): _has_source, _api_error,
|
SerdeError(SerdeErr): _has_source, _api_error,
|
||||||
JWTError(JWTErr): _has_source, _api_error,
|
JWtError(JwtErr): _has_source, _api_error,
|
||||||
TemplError(HbErr): _has_source, _api_error,
|
TemplError(HbErr): _has_source, _api_error,
|
||||||
//WsError(ws::Error): _has_source, _api_error,
|
//WsError(ws::Error): _has_source, _api_error,
|
||||||
IOError(IOErr): _has_source, _api_error,
|
IoError(IoErr): _has_source, _api_error,
|
||||||
TimeError(TimeErr): _has_source, _api_error,
|
TimeError(TimeErr): _has_source, _api_error,
|
||||||
ReqError(ReqErr): _has_source, _api_error,
|
ReqError(ReqErr): _has_source, _api_error,
|
||||||
RegexError(RegexErr): _has_source, _api_error,
|
RegexError(RegexErr): _has_source, _api_error,
|
||||||
|
@ -152,6 +152,7 @@ impl<S> MapResult<S> for Option<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
const fn _has_source<T>(e: T) -> Option<T> {
|
const fn _has_source<T>(e: T) -> Option<T> {
|
||||||
Some(e)
|
Some(e)
|
||||||
}
|
}
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -48,10 +48,7 @@ fn main() {
|
||||||
let level = LF::from_str(&CONFIG.log_level()).expect("Valid log level");
|
let level = LF::from_str(&CONFIG.log_level()).expect("Valid log level");
|
||||||
init_logging(level).ok();
|
init_logging(level).ok();
|
||||||
|
|
||||||
let extra_debug = match level {
|
let extra_debug = matches!(level, LF::Trace | LF::Debug);
|
||||||
LF::Trace | LF::Debug => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
check_data_folder();
|
check_data_folder();
|
||||||
check_rsa_keys();
|
check_rsa_keys();
|
||||||
|
@ -64,10 +61,10 @@ fn main() {
|
||||||
|
|
||||||
const HELP: &str = "\
|
const HELP: &str = "\
|
||||||
A Bitwarden API server written in Rust
|
A Bitwarden API server written in Rust
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
bitwarden_rs
|
bitwarden_rs
|
||||||
|
|
||||||
FLAGS:
|
FLAGS:
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
-v, --version Prints the app version
|
-v, --version Prints the app version
|
||||||
|
@ -329,7 +326,7 @@ fn launch_rocket(extra_debug: bool) {
|
||||||
.manage(pool)
|
.manage(pool)
|
||||||
.manage(api::start_notification_server())
|
.manage(api::start_notification_server())
|
||||||
.attach(util::AppHeaders())
|
.attach(util::AppHeaders())
|
||||||
.attach(util::CORS())
|
.attach(util::Cors())
|
||||||
.attach(util::BetterLogging(extra_debug))
|
.attach(util::BetterLogging(extra_debug))
|
||||||
.launch();
|
.launch();
|
||||||
|
|
||||||
|
|
16
src/util.rs
16
src/util.rs
|
@ -38,9 +38,9 @@ impl Fairing for AppHeaders {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CORS();
|
pub struct Cors();
|
||||||
|
|
||||||
impl CORS {
|
impl Cors {
|
||||||
fn get_header(headers: &HeaderMap, name: &str) -> String {
|
fn get_header(headers: &HeaderMap, name: &str) -> String {
|
||||||
match headers.get_one(name) {
|
match headers.get_one(name) {
|
||||||
Some(h) => h.to_string(),
|
Some(h) => h.to_string(),
|
||||||
|
@ -51,7 +51,7 @@ impl CORS {
|
||||||
// Check a request's `Origin` header against the list of allowed origins.
|
// Check a request's `Origin` header against the list of allowed origins.
|
||||||
// If a match exists, return it. Otherwise, return None.
|
// If a match exists, return it. Otherwise, return None.
|
||||||
fn get_allowed_origin(headers: &HeaderMap) -> Option<String> {
|
fn get_allowed_origin(headers: &HeaderMap) -> Option<String> {
|
||||||
let origin = CORS::get_header(headers, "Origin");
|
let origin = Cors::get_header(headers, "Origin");
|
||||||
let domain_origin = CONFIG.domain_origin();
|
let domain_origin = CONFIG.domain_origin();
|
||||||
let safari_extension_origin = "file://";
|
let safari_extension_origin = "file://";
|
||||||
if origin == domain_origin || origin == safari_extension_origin {
|
if origin == domain_origin || origin == safari_extension_origin {
|
||||||
|
@ -62,10 +62,10 @@ impl CORS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fairing for CORS {
|
impl Fairing for Cors {
|
||||||
fn info(&self) -> Info {
|
fn info(&self) -> Info {
|
||||||
Info {
|
Info {
|
||||||
name: "CORS",
|
name: "Cors",
|
||||||
kind: Kind::Response,
|
kind: Kind::Response,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,14 +73,14 @@ impl Fairing for CORS {
|
||||||
fn on_response(&self, request: &Request, response: &mut Response) {
|
fn on_response(&self, request: &Request, response: &mut Response) {
|
||||||
let req_headers = request.headers();
|
let req_headers = request.headers();
|
||||||
|
|
||||||
if let Some(origin) = CORS::get_allowed_origin(req_headers) {
|
if let Some(origin) = Cors::get_allowed_origin(req_headers) {
|
||||||
response.set_header(Header::new("Access-Control-Allow-Origin", origin));
|
response.set_header(Header::new("Access-Control-Allow-Origin", origin));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preflight request
|
// Preflight request
|
||||||
if request.method() == Method::Options {
|
if request.method() == Method::Options {
|
||||||
let req_allow_headers = CORS::get_header(req_headers, "Access-Control-Request-Headers");
|
let req_allow_headers = Cors::get_header(req_headers, "Access-Control-Request-Headers");
|
||||||
let req_allow_method = CORS::get_header(req_headers, "Access-Control-Request-Method");
|
let req_allow_method = Cors::get_header(req_headers, "Access-Control-Request-Method");
|
||||||
|
|
||||||
response.set_header(Header::new("Access-Control-Allow-Methods", req_allow_method));
|
response.set_header(Header::new("Access-Control-Allow-Methods", req_allow_method));
|
||||||
response.set_header(Header::new("Access-Control-Allow-Headers", req_allow_headers));
|
response.set_header(Header::new("Access-Control-Allow-Headers", req_allow_headers));
|
||||||
|
|
Laden …
In neuem Issue referenzieren