Browse Source

add required fields to schema/org form

pull/1955/head
Stuart Heap 4 years ago
parent
commit
2e907826ae
No known key found for this signature in database GPG Key ID: C753450AB379AA25
  1. 3
      .dockerignore
  2. 851
      Cargo.lock
  3. 3
      docker/amd64/Dockerfile
  4. 15
      migrations/mysql/2018-02-17-205753_create_collections_and_orgs/up.sql
  5. 17
      migrations/postgresql/2019-09-12-100000_create_tables/up.sql
  6. 15
      migrations/sqlite/2018-02-17-205753_create_collections_and_orgs/up.sql
  7. 46
      src/api/core/organizations.rs
  8. 59
      src/api/identity.rs
  9. 42
      src/db/models/organization.rs
  10. 9
      src/db/schemas/mysql/schema.rs
  11. 9
      src/db/schemas/postgresql/schema.rs
  12. 9
      src/db/schemas/sqlite/schema.rs

3
.dockerignore

@ -3,7 +3,6 @@ target
# Data folder # Data folder
data data
.env
.env.template .env.template
.gitattributes .gitattributes
@ -24,4 +23,4 @@ hooks
tools tools
# Web vault # Web vault
web-vault #web-vault

851
Cargo.lock

File diff suppressed because it is too large

3
docker/amd64/Dockerfile

@ -22,7 +22,6 @@
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:29a4fa7bf3790fff9d908b02ac5a154913491f4bf30c95b87b06d8cf1c5516b5 # $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:29a4fa7bf3790fff9d908b02ac5a154913491f4bf30c95b87b06d8cf1c5516b5
# [vaultwarden/web-vault:v2.21.1] # [vaultwarden/web-vault:v2.21.1]
# #
FROM vaultwarden/web-vault@sha256:29a4fa7bf3790fff9d908b02ac5a154913491f4bf30c95b87b06d8cf1c5516b5 as vault
########################## BUILD IMAGE ########################## ########################## BUILD IMAGE ##########################
FROM rust:1.53 as build FROM rust:1.53 as build
@ -101,7 +100,7 @@ EXPOSE 3012
# and the binary from the "build" stage to the current stage # and the binary from the "build" stage to the current stage
WORKDIR / WORKDIR /
COPY Rocket.toml . COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault COPY ./web-vault/build ./web-vault
COPY --from=build /app/target/release/vaultwarden . COPY --from=build /app/target/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh COPY docker/healthcheck.sh /healthcheck.sh

15
migrations/mysql/2018-02-17-205753_create_collections_and_orgs/up.sql

@ -5,9 +5,18 @@ CREATE TABLE collections (
); );
CREATE TABLE organizations ( CREATE TABLE organizations (
uuid VARCHAR(40) NOT NULL PRIMARY KEY, uuid VARCHAR(40) NOT NULL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
billing_email TEXT NOT NULL billing_email TEXT NOT NULL,
identifier TEXT NOT NULL,
use_sso BOOLEAN NOT NULL,
callback_path TEXT NOT NULL,
signed_out_callback_path TEXT NOT NULL,
authority TEXT NOT NULL,
client_id TEXT NOT NULL,
client_secret TEXT NOT NULL,
metadata_address TEXT NOT NULL,
oidc_redirect_behavior TEXT NOT NULL
); );
CREATE TABLE users_collections ( CREATE TABLE users_collections (

17
migrations/postgresql/2019-09-12-100000_create_tables/up.sql

@ -33,9 +33,18 @@ CREATE TABLE devices (
); );
CREATE TABLE organizations ( CREATE TABLE organizations (
uuid VARCHAR(40) NOT NULL PRIMARY KEY, uuid VARCHAR(40) NOT NULL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
billing_email TEXT NOT NULL billing_email TEXT NOT NULL,
identifier TEXT NOT NULL,
use_sso BOOLEAN NOT NULL,
callback_path TEXT NOT NULL,
signed_out_callback_path TEXT NOT NULL,
authority TEXT NOT NULL,
client_id TEXT NOT NULL,
client_secret TEXT NOT NULL,
metadata_address TEXT NOT NULL,
oidc_redirect_behavior TEXT NOT NULL
); );
CREATE TABLE ciphers ( CREATE TABLE ciphers (
@ -118,4 +127,4 @@ CREATE TABLE twofactor (
CREATE TABLE invitations ( CREATE TABLE invitations (
email VARCHAR(255) NOT NULL PRIMARY KEY email VARCHAR(255) NOT NULL PRIMARY KEY
); );

15
migrations/sqlite/2018-02-17-205753_create_collections_and_orgs/up.sql

@ -5,9 +5,18 @@ CREATE TABLE collections (
); );
CREATE TABLE organizations ( CREATE TABLE organizations (
uuid TEXT NOT NULL PRIMARY KEY, uuid TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
billing_email TEXT NOT NULL billing_email TEXT NOT NULL,
identifier TEXT NOT NULL,
use_sso BOOLEAN NOT NULL,
callback_path TEXT NOT NULL,
signed_out_callback_path TEXT NOT NULL,
authority TEXT NOT NULL,
client_id TEXT NOT NULL,
client_secret TEXT NOT NULL,
metadata_address TEXT NOT NULL,
oidc_redirect_behavior TEXT NOT NULL
); );

46
src/api/core/organizations.rs

@ -24,6 +24,7 @@ pub fn routes() -> Vec<Route> {
put_collection_users, put_collection_users,
put_organization, put_organization,
post_organization, post_organization,
put_organization_sso,
post_organization_collections, post_organization_collections,
delete_organization_collection_user, delete_organization_collection_user,
post_organization_collection_delete_user, post_organization_collection_delete_user,
@ -72,6 +73,20 @@ struct OrgData {
struct OrganizationUpdateData { struct OrganizationUpdateData {
BillingEmail: String, BillingEmail: String,
Name: String, Name: String,
Identifier: Option<String>,
}
#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
struct OrganizationSsoUpdateData {
UseSso: bool,
CallbackPath: String,
SignedOutCallbackPath: String,
Authority: String,
ClientId: String,
ClientSecret: String,
MetadataAddress: String,
OidcRedirectBehavior: String,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -200,6 +215,37 @@ fn post_organization(
org.name = data.Name; org.name = data.Name;
org.billing_email = data.BillingEmail; org.billing_email = data.BillingEmail;
org.identifier = match data.Identifier {
Some(identifier) => identifier,
None => String::from(""),
};
org.save(&conn)?;
Ok(Json(org.to_json()))
}
#[put("/organizations/<org_id>/sso", data = "<data>")]
fn put_organization_sso(
org_id: String,
_headers: OwnerHeaders,
data: JsonUpcase<OrganizationSsoUpdateData>,
conn: DbConn,
) -> JsonResult {
let data: OrganizationSsoUpdateData = data.into_inner().data;
let mut org = match Organization::find_by_uuid(&org_id, &conn) {
Some(organization) => organization,
None => err!("Can't find organization details"),
};
org.use_sso = data.UseSso;
org.callback_path = data.CallbackPath;
org.signed_out_callback_path = data.SignedOutCallbackPath;
org.authority = data.Authority;
org.client_id = data.ClientId;
org.client_secret = data.ClientSecret;
org.metadata_address = data.MetadataAddress;
org.oidc_redirect_behavior = data.OidcRedirectBehavior;
org.save(&conn)?; org.save(&conn)?;
Ok(Json(org.to_json())) Ok(Json(org.to_json()))

59
src/api/identity.rs

@ -1,6 +1,7 @@
use chrono::Local; use chrono::Local;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use rocket::{ use rocket::{
http::{RawStr, Status},
request::{Form, FormItems, FromForm}, request::{Form, FormItems, FromForm},
Route, Route,
}; };
@ -19,7 +20,7 @@ use crate::{
}; };
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![login] routes![login, prevalidate, authorize]
} }
#[post("/connect/token", data = "<data>")] #[post("/connect/token", data = "<data>")]
@ -421,7 +422,6 @@ impl<'f> FromForm<'f> for ConnectData {
key => warn!("Detected unexpected parameter during login: {}", key), key => warn!("Detected unexpected parameter during login: {}", key),
} }
} }
Ok(form) Ok(form)
} }
} }
@ -432,3 +432,58 @@ fn _check_is_some<T>(value: &Option<T>, msg: &str) -> EmptyResult {
} }
Ok(()) Ok(())
} }
fn invalid_json(error_message: &str, exception: bool) -> JsonResult {
if exception {
err_code!(error_message, Status::BadRequest.code)
}
err_code!(error_message, Status::InternalServerError.code)
}
#[get("/account/prevalidate?<domainHint>")]
#[allow(non_snake_case)]
fn prevalidate(domainHint: &RawStr, conn: DbConn) -> JsonResult {
let empty_result = json!({});
// TODO as_str shouldn't be used here
let organization = Organization::find_by_identifier(domainHint.as_str(), &conn);
match organization {
Some(organization) => {
if !organization.use_sso {
return invalid_json("SSO Not allowed for organization", false);
}
},
None => {
return invalid_json("Organization not found by identifier", false);
},
}
if domainHint == "" {
return invalid_json("No Organization Identifier Provided", false);
}
Ok(Json(empty_result))
}
#[get("/connect/authorize?<domain_hint>")]
fn authorize(
domain_hint: &RawStr,
conn: DbConn,
) {
let empty_result = json!({});
let organization = Organization::find_by_identifier(domain_hint.as_str(), &conn);
match organization {
Some(organization) => {
println!("found org. authority: {}", organization.authority);
let redirect = Some(organization.callback_path.to_string());
let issuer = reqwest::Url::parse(&organization.authority).unwrap();
println!("got issuer: {}", issuer);
// return Ok(Json(empty_result));
},
None => {
println!("error");
// return invalid_json("No Organization found", false);
}
}
}

42
src/db/models/organization.rs

@ -12,8 +12,17 @@ db_object! {
pub uuid: String, pub uuid: String,
pub name: String, pub name: String,
pub billing_email: String, pub billing_email: String,
pub identifier: String,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub use_sso: bool,
pub callback_path: String,
pub signed_out_callback_path: String,
pub authority: String,
pub client_id: String,
pub client_secret: String,
pub metadata_address: String,
pub oidc_redirect_behavior: String,
} }
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -131,13 +140,22 @@ impl Organization {
billing_email, billing_email,
private_key, private_key,
public_key, public_key,
identifier: String::from(""),
use_sso: false,
callback_path: String::from("http://localhost/oidc-signin"),
signed_out_callback_path: String::from("http://localhost/sso/oidc-signin"),
authority: String::from(""),
client_id: String::from(""),
client_secret: String::from(""),
metadata_address: String::from(""),
oidc_redirect_behavior: String::from(""),
} }
} }
pub fn to_json(&self) -> Value { pub fn to_json(&self) -> Value {
json!({ json!({
"Id": self.uuid, "Id": self.uuid,
"Identifier": null, // not supported by us "Identifier": self.identifier,
"Name": self.name, "Name": self.name,
"Seats": 10, // The value doesn't matter, we don't check server-side "Seats": 10, // The value doesn't matter, we don't check server-side
"MaxCollections": 10, // The value doesn't matter, we don't check server-side "MaxCollections": 10, // The value doesn't matter, we don't check server-side
@ -148,7 +166,7 @@ impl Organization {
"UseGroups": false, // not supported by us "UseGroups": false, // not supported by us
"UseTotp": true, "UseTotp": true,
"UsePolicies": true, "UsePolicies": true,
"UseSso": false, // We do not support SSO "UseSso": self.use_sso,
"SelfHost": true, "SelfHost": true,
"UseApi": false, // not supported by us "UseApi": false, // not supported by us
"HasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), "HasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(),
@ -166,6 +184,13 @@ impl Organization {
"PlanType": 5, // TeamsAnnually plan "PlanType": 5, // TeamsAnnually plan
"UsersGetPremium": true, "UsersGetPremium": true,
"Object": "organization", "Object": "organization",
"CallbackPath": self.callback_path,
"SignedOutCallbackPath": self.signed_out_callback_path,
"Authority": self.authority,
"ClientId": self.client_id,
"ClientSecret": self.client_secret,
"MetadataAddress": self.metadata_address,
"OidcRedirectBehavior": self.oidc_redirect_behavior,
}) })
} }
} }
@ -254,6 +279,15 @@ impl Organization {
}} }}
} }
pub fn find_by_identifier(identifier: &str, conn: &DbConn) -> Option<Self> {
db_run! { conn: {
organizations::table
.filter(organizations::identifier.eq(identifier))
.first::<OrganizationDb>(conn)
.ok().from_db()
}}
}
pub fn get_all(conn: &DbConn) -> Vec<Self> { pub fn get_all(conn: &DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
organizations::table.load::<OrganizationDb>(conn).expect("Error loading organizations").from_db() organizations::table.load::<OrganizationDb>(conn).expect("Error loading organizations").from_db()
@ -283,8 +317,8 @@ impl UserOrganization {
"SelfHost": true, "SelfHost": true,
"HasPublicAndPrivateKeys": org.private_key.is_some() && org.public_key.is_some(), "HasPublicAndPrivateKeys": org.private_key.is_some() && org.public_key.is_some(),
"ResetPasswordEnrolled": false, // not supported by us "ResetPasswordEnrolled": false, // not supported by us
"SsoBound": false, // We do not support SSO "SsoBound": true,
"UseSso": false, // We do not support SSO "UseSso": true,
// TODO: Add support for Business Portal // TODO: Add support for Business Portal
// Upstream is moving Policies and SSO management outside of the web-vault to /portal // Upstream is moving Policies and SSO management outside of the web-vault to /portal
// For now they still have that code also in the web-vault, but they will remove it at some point. // For now they still have that code also in the web-vault, but they will remove it at some point.

9
src/db/schemas/mysql/schema.rs

@ -100,8 +100,17 @@ table! {
uuid -> Text, uuid -> Text,
name -> Text, name -> Text,
billing_email -> Text, billing_email -> Text,
identifier -> Text,
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
use_sso -> Bool,
callback_path -> Text,
signed_out_callback_path -> Text,
authority -> Text,
client_id -> Text,
client_secret -> Text,
metadata_address -> Text,
oidc_redirect_behavior -> Text,
} }
} }

9
src/db/schemas/postgresql/schema.rs

@ -100,8 +100,17 @@ table! {
uuid -> Text, uuid -> Text,
name -> Text, name -> Text,
billing_email -> Text, billing_email -> Text,
identifier -> Text,
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
use_sso -> Bool,
callback_path -> Text,
signed_out_callback_path -> Text,
authority -> Text,
client_id -> Text,
client_secret -> Text,
metadata_address -> Text,
oidc_redirect_behavior -> Text,
} }
} }

9
src/db/schemas/sqlite/schema.rs

@ -100,8 +100,17 @@ table! {
uuid -> Text, uuid -> Text,
name -> Text, name -> Text,
billing_email -> Text, billing_email -> Text,
identifier -> Text,
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Nullable<Text>, public_key -> Nullable<Text>,
use_sso -> Bool,
callback_path -> Text,
signed_out_callback_path -> Text,
authority -> Text,
client_id -> Text,
client_secret -> Text,
metadata_address -> Text,
oidc_redirect_behavior -> Text,
} }
} }

Loading…
Cancel
Save