commit 1bc346688c6dfbb34b61c72d1539faf3e264c98a
parent d6a1a9b27471d4d861f6c8756860db8d08faa246
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Sat, 17 Feb 2018 22:30:19 +0100
Some initial work on organizations, nothing works yet
Diffstat:
7 files changed, 357 insertions(+), 12 deletions(-)
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
@@ -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
@@ -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
@@ -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<Route> {
@@ -49,7 +51,7 @@ pub fn routes() -> Vec<Route> {
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/<uuid>/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
@@ -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 = "<data>")]
+fn create_organization(headers: Headers, data: Json<Value>, conn: DbConn) -> JsonResult {
+ /*
+ Data is a JSON Object with the following entries
+ billingEmail <email>
+ collectionName <encrypted_collection_name>
+ key <key>
+ name <unencrypted_name>
+ planType free
+ */
+
+ // We need to add the following key to the users jwt claims
+ // orgowner: "<org-id>"
+
+ // 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/<org_id>/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/<org_id>/collections/<coll_id>/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: <id>
+ AccessAll: true
+ Name: <user_name>
+ Email: <user_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
@@ -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<Self> {
+ collections::table
+ .filter(collections::uuid.eq(uuid))
+ .first::<Self>(&**conn).ok()
+ }
+}
diff --git a/src/db/models/org/organization.rs 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<Self> {
+ organizations::table
+ .filter(organizations::uuid.eq(uuid))
+ .first::<Self>(&**conn).ok()
+ }
+}
diff --git a/src/db/schema.rs b/src/db/schema.rs
@@ -23,6 +23,14 @@ table! {
}
table! {
+ collections (uuid) {
+ uuid -> Text,
+ org_uuid -> Text,
+ name -> Text,
+ }
+}
+
+table! {
devices (uuid) {
uuid -> Text,
created_at -> Timestamp,
@@ -47,6 +55,14 @@ table! {
}
table! {
+ organizations (uuid) {
+ uuid -> Text,
+ name -> Text,
+ billing_email -> Text,
+ }
+}
+
+table! {
users (uuid) {
uuid -> Text,
created_at -> Timestamp,
@@ -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,
);