From 1bc346688c6dfbb34b61c72d1539faf3e264c98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Sat, 17 Feb 2018 22:30:19 +0100 Subject: [PATCH] Some initial work on organizations, nothing works yet --- .../down.sql | 8 ++ .../up.sql | 29 +++++ src/api/core/mod.rs | 15 +-- src/api/core/organizations.rs | 85 +++++++++++++ src/db/models/org/collection.rs | 73 +++++++++++ src/db/models/org/organization.rs | 116 ++++++++++++++++++ src/db/schema.rs | 43 +++++++ 7 files changed, 357 insertions(+), 12 deletions(-) create mode 100644 migrations/2018-02-17-205753_create_collections_and_orgs/down.sql create mode 100644 migrations/2018-02-17-205753_create_collections_and_orgs/up.sql create mode 100644 src/api/core/organizations.rs create mode 100644 src/db/models/org/collection.rs create mode 100644 src/db/models/org/organization.rs diff --git a/migrations/2018-02-17-205753_create_collections_and_orgs/down.sql b/migrations/2018-02-17-205753_create_collections_and_orgs/down.sql new file mode 100644 index 00000000..b6446cd8 --- /dev/null +++ b/migrations/2018-02-17-205753_create_collections_and_orgs/down.sql @@ -0,0 +1,8 @@ +DROP TABLE collections; + +DROP TABLE organizations; + + +DROP TABLE users_collections; + +DROP TABLE users_organizations; diff --git a/migrations/2018-02-17-205753_create_collections_and_orgs/up.sql b/migrations/2018-02-17-205753_create_collections_and_orgs/up.sql new file mode 100644 index 00000000..a7b32320 --- /dev/null +++ b/migrations/2018-02-17-205753_create_collections_and_orgs/up.sql @@ -0,0 +1,29 @@ +CREATE TABLE collections ( + uuid TEXT NOT NULL PRIMARY KEY, + org_uuid TEXT NOT NULL REFERENCES organizations (uuid), + name TEXT NOT NULL +); + +CREATE TABLE organizations ( + uuid TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + billing_email TEXT NOT NULL +); + + +CREATE TABLE users_collections ( + user_uuid TEXT NOT NULL REFERENCES users (uuid), + collection_uuid TEXT NOT NULL REFERENCES collections (uuid), + PRIMARY KEY (user_uuid, collection_uuid) +); + +CREATE TABLE users_organizations ( + user_uuid TEXT NOT NULL REFERENCES users (uuid), + org_uuid TEXT NOT NULL REFERENCES organizations (uuid), + + key TEXT NOT NULL, + status INTEGER NOT NULL, + type INTEGER NOT NULL, + + PRIMARY KEY (user_uuid, org_uuid) +); diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index e0dfaa17..7d4f1461 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -1,11 +1,13 @@ mod accounts; mod ciphers; mod folders; +mod organizations; mod two_factor; use self::accounts::*; use self::ciphers::*; use self::folders::*; +use self::organizations::*; use self::two_factor::*; pub fn routes() -> Vec { @@ -49,7 +51,7 @@ pub fn routes() -> Vec { activate_authenticator, disable_authenticator, - get_collections, + get_user_collections, clear_device_token, put_device_token, @@ -72,17 +74,6 @@ use db::DbConn; use api::JsonResult; use auth::Headers; - -// GET /api/collections?writeOnly=false -#[get("/collections")] -fn get_collections() -> JsonResult { - Ok(Json(json!({ - "Data": [], - "Object": "list" - }))) -} - - #[put("/devices/identifier//clear-token")] fn clear_device_token(uuid: String, conn: DbConn) -> JsonResult { err!("Not implemented") diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs new file mode 100644 index 00000000..97b93e2f --- /dev/null +++ b/src/api/core/organizations.rs @@ -0,0 +1,85 @@ +use rocket_contrib::{Json, Value}; + +use db::DbConn; +use db::models::*; + +use api::{JsonResult, EmptyResult}; +use auth::Headers; + +#[post("/organizations", data = "")] +fn create_organization(headers: Headers, data: Json, conn: DbConn) -> JsonResult { + /* + Data is a JSON Object with the following entries + billingEmail + collectionName + key + name + planType free + */ + + // We need to add the following key to the users jwt claims + // orgowner: "" + + // This function returns organization.to_json(); + err!("Not implemented") +} + + +// GET /api/collections?writeOnly=false +#[get("/collections")] +fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult { + + // let collections_json = get_user_collections().map(|c|c.to_json()); + + Ok(Json(json!({ + "Data": [], + "Object": "list" + }))) +} + +#[get("/organizations//collections")] +fn get_org_collections(org_id: String, headers: Headers, conn: DbConn) -> JsonResult { + // let org = get_org_by_id(org_id) + // let collections_json = org.collections().map(|c|c.to_json()); + + Ok(Json(json!({ + "Data": [], + "Object": "list" + }))) +} + + +#[get("/organizations//collections//users")] +fn get_collection_users(org_id: String, coll_id: String, headers: Headers, conn: DbConn) -> JsonResult { + // Get org and collection, check that collection is from org + + // Get the users from collection + + /* + The elements from the data array to return have the following structure + + { + OrganizationUserId: + AccessAll: true + Name: + Email: + Type: 0 + Status: 2 + ReadOnly: false + Object: collectionUser + } + */ + + Ok(Json(json!({ + "Data": [], + "Object": "list" + }))) +} + + +//******************************************************************************************** +/* + We need to modify 'GET /api/profile' to return the users organizations, instead of [] + + The elements from that array come from organization.to_json_profile() +*/ diff --git a/src/db/models/org/collection.rs b/src/db/models/org/collection.rs new file mode 100644 index 00000000..5c591532 --- /dev/null +++ b/src/db/models/org/collection.rs @@ -0,0 +1,73 @@ +use chrono::{NaiveDateTime, Utc}; +use serde_json::Value as JsonValue; + +use uuid::Uuid; + +use super::Organization; + +#[derive(Debug, Identifiable, Queryable, Insertable, Associations)] +#[table_name = "collections"] +#[belongs_to(Organization, foreign_key = "org_uuid")] +#[primary_key(uuid)] +pub struct Collection { + pub uuid: String, + pub org_uuid: String, + pub name: String, +} + +/// Local methods +impl Collection { + pub fn new(org_uuid: String, name: String) -> Self { + let now = Utc::now().naive_utc(); + + Self { + uuid: Uuid::new_v4().to_string(), + + org_uuid, + name, + } + } + + pub fn to_json(&self) -> JsonValue { + json!({ + "Id": self.uuid, + "OrganizationId": self.org_uuid, + "Name": self.name, + "Object": "collection", + }) + } +} + +use diesel; +use diesel::prelude::*; +use db::DbConn; +use db::schema::collections; + +/// Database methods +impl Collection { + pub fn save(&mut self, conn: &DbConn) -> bool { + self.updated_at = Utc::now().naive_utc(); + + match diesel::replace_into(collections::table) + .values(&*self) + .execute(&**conn) { + Ok(1) => true, // One row inserted + _ => false, + } + } + + pub fn delete(self, conn: &DbConn) -> bool { + match diesel::delete(collections::table.filter( + collections::uuid.eq(self.uuid))) + .execute(&**conn) { + Ok(1) => true, // One row deleted + _ => false, + } + } + + pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + collections::table + .filter(collections::uuid.eq(uuid)) + .first::(&**conn).ok() + } +} diff --git a/src/db/models/org/organization.rs b/src/db/models/org/organization.rs new file mode 100644 index 00000000..22a2e0ca --- /dev/null +++ b/src/db/models/org/organization.rs @@ -0,0 +1,116 @@ +use chrono::{NaiveDateTime, Utc}; +use serde_json::Value as JsonValue; + +use uuid::Uuid; + +#[derive(Debug, Identifiable, Queryable, Insertable)] +#[table_name = "organizations"] +#[primary_key(uuid)] +pub struct Organization { + pub uuid: String, + pub name: String, + pub billing_email: String, + + pub key: String, +} + +/// Local methods +impl Organization { + pub fn new(name: String, billing_email: String, key: String) -> Self { + let now = Utc::now().naive_utc(); + + Self { + uuid: Uuid::new_v4().to_string(), + + name, + billing_email, + key, + } + } + + pub fn to_json(&self) -> JsonValue { + json!({ + "Id": self.uuid, + "Name": self.name, + + "BusinessName": null, + "BusinessAddress1": null, + "BusinessAddress2": null, + "BusinessAddress3": null, + "BusinessCountry": null, + "BusinessTaxNumber": null, + "BillingEmail":self.billing_email, + "Plan": "Free", + "PlanType": 0, // Free plan + + "Seats": 10, + "MaxCollections": 10, + + "UseGroups": false, + "UseDirectory": false, + "UseEvents": false, + "UseTotp": false, + + "Object": "organization", + }) + } + + pub fn to_json_profile(&self) -> JsonValue { + json!({ + "Id": self.uuid, + "Name": self.name, + + "Seats": 10, + "MaxCollections": 10, + + "UseGroups": false, + "UseDirectory": false, + "UseEvents": false, + "UseTotp": false, + + "MaxStorageGb": null, + + // These are probably per user + "Key": self.key, + "Status": 2, // Confirmed + "Type": 0, // Owner + "Enabled": true, + + "Object": "profileOrganization", + }) + } +} + +use diesel; +use diesel::prelude::*; +use db::DbConn; +use db::schema::organizations; + +/// Database methods +impl Organization { + pub fn save(&mut self, conn: &DbConn) -> bool { + self.updated_at = Utc::now().naive_utc(); + + match diesel::replace_into(organizations::table) + .values(&*self) + .execute(&**conn) { + Ok(1) => true, // One row inserted + _ => false, + } + } + + pub fn delete(self, conn: &DbConn) -> bool { + match diesel::delete(organizations::table.filter( + organizations::uuid.eq(self.uuid))) + .execute(&**conn) { + Ok(1) => true, // One row deleted + _ => false, + } + } + + pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + organizations::table + .filter(organizations::uuid.eq(uuid)) + .first::(&**conn).ok() + } +} diff --git a/src/db/schema.rs b/src/db/schema.rs index 9f0ca0f6..7e48ca72 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -22,6 +22,14 @@ table! { } } +table! { + collections (uuid) { + uuid -> Text, + org_uuid -> Text, + name -> Text, + } +} + table! { devices (uuid) { uuid -> Text, @@ -46,6 +54,14 @@ table! { } } +table! { + organizations (uuid) { + uuid -> Text, + name -> Text, + billing_email -> Text, + } +} + table! { users (uuid) { uuid -> Text, @@ -68,16 +84,43 @@ table! { } } +table! { + users_collections (user_uuid, collection_uuid) { + user_uuid -> Text, + collection_uuid -> Text, + } +} + +table! { + users_organizations (user_uuid, org_uuid) { + user_uuid -> Text, + org_uuid -> Text, + key -> Text, + status -> Integer, + #[sql_name = "type"] + type_ -> Integer, + } +} + joinable!(attachments -> ciphers (cipher_uuid)); joinable!(ciphers -> folders (folder_uuid)); joinable!(ciphers -> users (user_uuid)); +joinable!(collections -> organizations (org_uuid)); joinable!(devices -> users (user_uuid)); joinable!(folders -> users (user_uuid)); +joinable!(users_collections -> collections (collection_uuid)); +joinable!(users_collections -> users (user_uuid)); +joinable!(users_organizations -> organizations (org_uuid)); +joinable!(users_organizations -> users (user_uuid)); allow_tables_to_appear_in_same_query!( attachments, ciphers, + collections, devices, folders, + organizations, users, + users_collections, + users_organizations, );