2020-07-14 18:00:09 +02:00
use num_traits ::FromPrimitive ;
use rocket ::{ request ::Form , Route } ;
2018-10-10 20:40:39 +02:00
use rocket_contrib ::json ::Json ;
use serde_json ::Value ;
2020-07-14 18:00:09 +02:00
use crate ::{
api ::{ EmptyResult , JsonResult , JsonUpcase , JsonUpcaseVec , Notify , NumberOrString , PasswordData , UpdateType } ,
2021-03-31 22:18:35 +02:00
auth ::{ decode_invite , AdminHeaders , Headers , ManagerHeaders , ManagerHeadersLoose , OwnerHeaders } ,
2020-07-14 18:00:09 +02:00
db ::{ models ::* , DbConn } ,
mail , CONFIG ,
2019-01-25 17:43:51 +01:00
} ;
2018-10-10 20:40:39 +02:00
pub fn routes ( ) -> Vec < Route > {
routes! [
get_organization ,
create_organization ,
delete_organization ,
post_delete_organization ,
leave_organization ,
get_user_collections ,
get_org_collections ,
get_org_collection_detail ,
get_collection_users ,
2019-01-25 17:43:51 +01:00
put_collection_users ,
2018-10-10 20:40:39 +02:00
put_organization ,
post_organization ,
post_organization_collections ,
delete_organization_collection_user ,
post_organization_collection_delete_user ,
post_organization_collection_update ,
put_organization_collection_update ,
delete_organization_collection ,
post_organization_collection_delete ,
get_org_details ,
get_org_users ,
send_invite ,
2018-12-30 05:24:38 +01:00
reinvite_user ,
2018-10-10 20:40:39 +02:00
confirm_invite ,
2018-12-15 03:56:00 +01:00
accept_invite ,
2018-10-10 20:40:39 +02:00
get_user ,
edit_user ,
put_organization_user ,
delete_user ,
post_delete_user ,
post_org_import ,
2020-03-14 13:22:30 +01:00
list_policies ,
2020-03-20 10:51:17 +01:00
list_policies_token ,
2020-03-14 13:22:30 +01:00
get_policy ,
put_policy ,
2021-01-31 21:46:37 +01:00
get_organization_tax ,
2020-09-14 08:34:17 +02:00
get_plans ,
2021-01-31 21:46:37 +01:00
get_plans_tax_rates ,
2021-02-06 18:22:39 +01:00
import ,
2018-10-10 20:40:39 +02:00
]
}
2018-04-24 22:01:55 +02:00
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
struct OrgData {
2018-06-01 00:18:50 +02:00
BillingEmail : String ,
CollectionName : String ,
Key : String ,
Name : String ,
#[ serde(rename = " PlanType " ) ]
2018-07-21 17:27:00 +02:00
_PlanType : NumberOrString , // Ignored, always use the same plan
2018-04-24 22:01:55 +02:00
}
2018-04-20 18:35:11 +02:00
#[ derive(Deserialize, Debug) ]
#[ allow(non_snake_case) ]
struct OrganizationUpdateData {
2018-06-01 00:18:50 +02:00
BillingEmail : String ,
Name : String ,
2018-04-20 18:35:11 +02:00
}
#[ derive(Deserialize, Debug) ]
#[ allow(non_snake_case) ]
struct NewCollectionData {
2018-06-01 00:18:50 +02:00
Name : String ,
2018-04-20 18:35:11 +02:00
}
2018-02-17 22:30:19 +01:00
#[ post( " /organizations " , data = " <data> " ) ]
2018-06-01 00:18:50 +02:00
fn create_organization ( headers : Headers , data : JsonUpcase < OrgData > , conn : DbConn ) -> JsonResult {
2020-08-06 07:35:29 +02:00
if ! CONFIG . is_org_creation_allowed ( & headers . user . email ) {
err! ( " User not allowed to create organizations " )
}
2018-06-01 00:18:50 +02:00
let data : OrgData = data . into_inner ( ) . data ;
2018-02-17 22:30:19 +01:00
2019-02-22 20:25:50 +01:00
let org = Organization ::new ( data . Name , data . BillingEmail ) ;
2019-11-02 17:39:01 +01:00
let mut user_org = UserOrganization ::new ( headers . user . uuid , org . uuid . clone ( ) ) ;
2019-02-22 20:25:50 +01:00
let collection = Collection ::new ( org . uuid . clone ( ) , data . CollectionName ) ;
2018-02-17 22:30:19 +01:00
2019-05-20 21:24:29 +02:00
user_org . akey = data . Key ;
2018-04-24 22:01:55 +02:00
user_org . access_all = true ;
2019-05-20 21:24:29 +02:00
user_org . atype = UserOrgType ::Owner as i32 ;
2018-04-24 22:01:55 +02:00
user_org . status = UserOrgStatus ::Confirmed as i32 ;
2018-12-19 21:52:53 +01:00
org . save ( & conn ) ? ;
user_org . save ( & conn ) ? ;
collection . save ( & conn ) ? ;
2018-04-24 22:01:55 +02:00
Ok ( Json ( org . to_json ( ) ) )
2018-02-17 22:30:19 +01:00
}
2018-08-13 17:45:30 +02:00
#[ delete( " /organizations/<org_id> " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn delete_organization (
org_id : String ,
data : JsonUpcase < PasswordData > ,
headers : OwnerHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-06-01 00:18:50 +02:00
let data : PasswordData = data . into_inner ( ) . data ;
let password_hash = data . MasterPasswordHash ;
2018-04-25 00:34:40 +02:00
2018-05-18 17:52:51 +02:00
if ! headers . user . check_valid_password ( & password_hash ) {
err! ( " Invalid password " )
}
match Organization ::find_by_uuid ( & org_id , & conn ) {
None = > err! ( " Organization not found " ) ,
2018-12-30 23:34:31 +01:00
Some ( org ) = > org . delete ( & conn ) ,
2018-05-18 17:52:51 +02:00
}
2018-04-25 00:34:40 +02:00
}
2018-08-13 17:45:30 +02:00
#[ post( " /organizations/<org_id>/delete " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn post_delete_organization (
org_id : String ,
data : JsonUpcase < PasswordData > ,
headers : OwnerHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-08-13 17:45:30 +02:00
delete_organization ( org_id , data , headers , conn )
}
2018-07-11 16:30:03 +02:00
#[ post( " /organizations/<org_id>/leave " ) ]
fn leave_organization ( org_id : String , headers : Headers , conn : DbConn ) -> EmptyResult {
match UserOrganization ::find_by_user_and_org ( & headers . user . uuid , & org_id , & conn ) {
None = > err! ( " User not part of organization " ) ,
Some ( user_org ) = > {
2019-05-20 21:24:29 +02:00
if user_org . atype = = UserOrgType ::Owner {
2018-12-30 23:34:31 +01:00
let num_owners =
UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::Owner as i32 , & conn ) . len ( ) ;
2018-07-11 16:30:03 +02:00
if num_owners < = 1 {
err! ( " The last owner can't leave " )
}
}
2018-12-30 23:34:31 +01:00
2018-12-19 21:52:53 +01:00
user_org . delete ( & conn )
2018-07-11 16:30:03 +02:00
}
}
}
2018-04-20 18:35:11 +02:00
#[ get( " /organizations/<org_id> " ) ]
2018-05-30 22:30:45 +02:00
fn get_organization ( org_id : String , _headers : OwnerHeaders , conn : DbConn ) -> JsonResult {
2018-04-20 18:35:11 +02:00
match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( organization ) = > Ok ( Json ( organization . to_json ( ) ) ) ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Can't find organization details " ) ,
2018-04-20 18:35:11 +02:00
}
}
2018-08-21 14:25:52 +02:00
#[ put( " /organizations/<org_id> " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn put_organization (
org_id : String ,
headers : OwnerHeaders ,
data : JsonUpcase < OrganizationUpdateData > ,
conn : DbConn ,
) -> JsonResult {
2018-08-21 14:25:52 +02:00
post_organization ( org_id , headers , data , conn )
}
2018-04-20 18:35:11 +02:00
#[ post( " /organizations/<org_id> " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn post_organization (
org_id : String ,
_headers : OwnerHeaders ,
data : JsonUpcase < OrganizationUpdateData > ,
conn : DbConn ,
) -> JsonResult {
2018-06-01 00:18:50 +02:00
let data : OrganizationUpdateData = data . into_inner ( ) . data ;
2018-04-20 18:35:11 +02:00
let mut org = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( organization ) = > organization ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Can't find organization details " ) ,
2018-04-20 18:35:11 +02:00
} ;
2018-06-01 00:18:50 +02:00
org . name = data . Name ;
org . billing_email = data . BillingEmail ;
2018-04-20 18:35:11 +02:00
2018-12-19 21:52:53 +01:00
org . save ( & conn ) ? ;
Ok ( Json ( org . to_json ( ) ) )
2018-04-20 18:35:11 +02:00
}
2018-02-17 22:30:19 +01:00
// GET /api/collections?writeOnly=false
#[ get( " /collections " ) ]
2021-03-27 16:07:26 +01:00
fn get_user_collections ( headers : Headers , conn : DbConn ) -> Json < Value > {
Json ( json! ( {
2018-04-20 18:35:11 +02:00
" Data " :
Collection ::find_by_user_uuid ( & headers . user . uuid , & conn )
. iter ( )
2018-09-13 15:16:24 +02:00
. map ( Collection ::to_json )
. collect ::< Value > ( ) ,
2018-10-01 18:02:58 +02:00
" Object " : " list " ,
" ContinuationToken " : null ,
2021-03-27 16:07:26 +01:00
} ) )
2018-02-17 22:30:19 +01:00
}
#[ get( " /organizations/<org_id>/collections " ) ]
2021-03-27 16:07:26 +01:00
fn get_org_collections ( org_id : String , _headers : AdminHeaders , conn : DbConn ) -> Json < Value > {
Json ( json! ( {
2018-04-20 18:35:11 +02:00
" Data " :
2018-05-11 20:08:02 +02:00
Collection ::find_by_organization ( & org_id , & conn )
2018-04-20 18:35:11 +02:00
. iter ( )
2018-09-13 15:16:24 +02:00
. map ( Collection ::to_json )
. collect ::< Value > ( ) ,
2018-10-01 18:02:58 +02:00
" Object " : " list " ,
" ContinuationToken " : null ,
2021-03-27 16:07:26 +01:00
} ) )
2018-02-17 22:30:19 +01:00
}
2018-04-20 18:35:11 +02:00
#[ post( " /organizations/<org_id>/collections " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn post_organization_collections (
org_id : String ,
2020-12-02 22:50:51 +01:00
headers : ManagerHeadersLoose ,
2018-12-30 23:34:31 +01:00
data : JsonUpcase < NewCollectionData > ,
conn : DbConn ,
) -> JsonResult {
2018-06-01 00:18:50 +02:00
let data : NewCollectionData = data . into_inner ( ) . data ;
2018-04-20 18:35:11 +02:00
let org = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( organization ) = > organization ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Can't find organization details " ) ,
2018-04-20 18:35:11 +02:00
} ;
2020-12-02 22:50:51 +01:00
// Get the user_organization record so that we can check if the user has access to all collections.
let user_org = match UserOrganization ::find_by_user_and_org ( & headers . user . uuid , & org_id , & conn ) {
Some ( u ) = > u ,
None = > err! ( " User is not part of organization " ) ,
} ;
2019-11-02 17:39:01 +01:00
let collection = Collection ::new ( org . uuid , data . Name ) ;
2018-12-19 21:52:53 +01:00
collection . save ( & conn ) ? ;
2018-05-04 19:25:50 +02:00
2020-12-02 22:50:51 +01:00
// If the user doesn't have access to all collections, only in case of a Manger,
// then we need to save the creating user uuid (Manager) to the users_collection table.
// Else the user will not have access to his own created collection.
if ! user_org . access_all {
CollectionUser ::save ( & headers . user . uuid , & collection . uuid , false , false , & conn ) ? ;
}
2018-04-20 18:35:11 +02:00
Ok ( Json ( collection . to_json ( ) ) )
}
2018-08-13 17:45:30 +02:00
#[ put( " /organizations/<org_id>/collections/<col_id> " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn put_organization_collection_update (
org_id : String ,
col_id : String ,
2020-12-02 22:50:51 +01:00
headers : ManagerHeaders ,
2018-12-30 23:34:31 +01:00
data : JsonUpcase < NewCollectionData > ,
conn : DbConn ,
) -> JsonResult {
2018-08-13 17:45:30 +02:00
post_organization_collection_update ( org_id , col_id , headers , data , conn )
}
2018-04-20 18:35:11 +02:00
#[ post( " /organizations/<org_id>/collections/<col_id> " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn post_organization_collection_update (
org_id : String ,
col_id : String ,
2020-12-02 22:50:51 +01:00
_headers : ManagerHeaders ,
2018-12-30 23:34:31 +01:00
data : JsonUpcase < NewCollectionData > ,
conn : DbConn ,
) -> JsonResult {
2018-06-01 00:18:50 +02:00
let data : NewCollectionData = data . into_inner ( ) . data ;
2018-04-20 18:35:11 +02:00
let org = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( organization ) = > organization ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Can't find organization details " ) ,
2018-04-20 18:35:11 +02:00
} ;
let mut collection = match Collection ::find_by_uuid ( & col_id , & conn ) {
Some ( collection ) = > collection ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Collection not found " ) ,
2018-04-20 18:35:11 +02:00
} ;
2018-05-30 22:30:45 +02:00
if collection . org_uuid ! = org . uuid {
err! ( " Collection is not owned by organization " ) ;
}
2019-11-02 17:39:01 +01:00
collection . name = data . Name ;
2018-12-19 21:52:53 +01:00
collection . save ( & conn ) ? ;
2018-04-20 18:35:11 +02:00
Ok ( Json ( collection . to_json ( ) ) )
}
2018-08-13 17:45:30 +02:00
#[ delete( " /organizations/<org_id>/collections/<col_id>/user/<org_user_id> " ) ]
2018-12-30 23:34:31 +01:00
fn delete_organization_collection_user (
org_id : String ,
col_id : String ,
org_user_id : String ,
_headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-05-30 17:01:56 +02:00
let collection = match Collection ::find_by_uuid ( & col_id , & conn ) {
None = > err! ( " Collection not found " ) ,
2018-12-30 23:34:31 +01:00
Some ( collection ) = > {
if collection . org_uuid = = org_id {
collection
} else {
err! ( " Collection and Organization id do not match " )
}
2018-05-30 17:01:56 +02:00
}
} ;
2018-09-04 14:37:44 +02:00
match UserOrganization ::find_by_uuid_and_org ( & org_user_id , & org_id , & conn ) {
2018-05-30 17:01:56 +02:00
None = > err! ( " User not found in organization " ) ,
Some ( user_org ) = > {
match CollectionUser ::find_by_collection_and_user ( & collection . uuid , & user_org . user_uuid , & conn ) {
None = > err! ( " User not assigned to collection " ) ,
2018-12-30 23:34:31 +01:00
Some ( col_user ) = > col_user . delete ( & conn ) ,
2018-05-29 17:01:38 +02:00
}
}
}
}
2018-08-13 17:45:30 +02:00
#[ post( " /organizations/<org_id>/collections/<col_id>/delete-user/<org_user_id> " ) ]
2018-12-30 23:34:31 +01:00
fn post_organization_collection_delete_user (
org_id : String ,
col_id : String ,
org_user_id : String ,
headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-08-13 17:45:30 +02:00
delete_organization_collection_user ( org_id , col_id , org_user_id , headers , conn )
2018-05-17 00:05:50 +02:00
}
2018-08-13 17:45:30 +02:00
#[ delete( " /organizations/<org_id>/collections/<col_id> " ) ]
2021-03-31 22:18:35 +02:00
fn delete_organization_collection (
org_id : String ,
col_id : String ,
_headers : ManagerHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-05-30 18:12:18 +02:00
match Collection ::find_by_uuid ( & col_id , & conn ) {
None = > err! ( " Collection not found " ) ,
2018-12-30 23:34:31 +01:00
Some ( collection ) = > {
if collection . org_uuid = = org_id {
collection . delete ( & conn )
} else {
err! ( " Collection and Organization id do not match " )
}
2018-05-17 00:05:50 +02:00
}
}
}
2018-08-13 17:45:30 +02:00
#[ derive(Deserialize, Debug) ]
#[ allow(non_snake_case) ]
struct DeleteCollectionData {
Id : String ,
OrgId : String ,
}
#[ post( " /organizations/<org_id>/collections/<col_id>/delete " , data = " <_data> " ) ]
2018-12-30 23:34:31 +01:00
fn post_organization_collection_delete (
org_id : String ,
col_id : String ,
2020-12-02 22:50:51 +01:00
headers : ManagerHeaders ,
2018-12-30 23:34:31 +01:00
_data : JsonUpcase < DeleteCollectionData > ,
conn : DbConn ,
) -> EmptyResult {
2018-08-13 17:45:30 +02:00
delete_organization_collection ( org_id , col_id , headers , conn )
}
2018-04-20 18:35:11 +02:00
#[ get( " /organizations/<org_id>/collections/<coll_id>/details " ) ]
2020-12-02 22:50:51 +01:00
fn get_org_collection_detail ( org_id : String , coll_id : String , headers : ManagerHeaders , conn : DbConn ) -> JsonResult {
2018-04-20 18:35:11 +02:00
match Collection ::find_by_uuid_and_user ( & coll_id , & headers . user . uuid , & conn ) {
None = > err! ( " Collection not found " ) ,
2018-05-30 22:30:45 +02:00
Some ( collection ) = > {
if collection . org_uuid ! = org_id {
err! ( " Collection is not owned by organization " )
}
Ok ( Json ( collection . to_json ( ) ) )
}
2018-04-20 18:35:11 +02:00
}
}
2018-04-25 00:34:40 +02:00
#[ get( " /organizations/<org_id>/collections/<coll_id>/users " ) ]
2020-12-02 22:50:51 +01:00
fn get_collection_users ( org_id : String , coll_id : String , _headers : ManagerHeaders , conn : DbConn ) -> JsonResult {
2018-04-25 00:34:40 +02:00
// Get org and collection, check that collection is from org
2018-05-29 17:01:38 +02:00
let collection = match Collection ::find_by_uuid_and_org ( & coll_id , & org_id , & conn ) {
None = > err! ( " Collection not found in Organization " ) ,
2018-12-30 23:34:31 +01:00
Some ( collection ) = > collection ,
2018-05-29 17:01:38 +02:00
} ;
2018-04-25 00:34:40 +02:00
// Get the users from collection
2018-05-29 17:01:38 +02:00
let user_list : Vec < Value > = CollectionUser ::find_by_collection ( & collection . uuid , & conn )
2018-12-30 23:34:31 +01:00
. iter ( )
. map ( | col_user | {
UserOrganization ::find_by_user_and_org ( & col_user . user_uuid , & org_id , & conn )
. unwrap ( )
2020-07-03 06:51:20 +02:00
. to_json_user_access_restrictions ( & col_user )
2018-12-30 23:34:31 +01:00
} )
. collect ( ) ;
2018-04-25 00:34:40 +02:00
2019-01-25 15:18:06 +01:00
Ok ( Json ( json! ( user_list ) ) )
2018-04-25 00:34:40 +02:00
}
2019-01-25 17:43:51 +01:00
#[ put( " /organizations/<org_id>/collections/<coll_id>/users " , data = " <data> " ) ]
fn put_collection_users (
org_id : String ,
coll_id : String ,
data : JsonUpcaseVec < CollectionData > ,
2020-12-02 22:50:51 +01:00
_headers : ManagerHeaders ,
2019-01-25 17:43:51 +01:00
conn : DbConn ,
) -> EmptyResult {
// Get org and collection, check that collection is from org
if Collection ::find_by_uuid_and_org ( & coll_id , & org_id , & conn ) . is_none ( ) {
err! ( " Collection not found in Organization " )
}
// Delete all the user-collections
CollectionUser ::delete_all_by_collection ( & coll_id , & conn ) ? ;
// And then add all the received ones (except if the user has access_all)
for d in data . iter ( ) . map ( | d | & d . data ) {
let user = match UserOrganization ::find_by_uuid ( & d . Id , & conn ) {
Some ( u ) = > u ,
None = > err! ( " User is not part of organization " ) ,
} ;
if user . access_all {
continue ;
}
2021-03-31 22:18:35 +02:00
CollectionUser ::save ( & user . user_uuid , & coll_id , d . ReadOnly , d . HidePasswords , & conn ) ? ;
2019-01-25 17:43:51 +01:00
}
Ok ( ( ) )
}
2018-04-24 22:01:55 +02:00
#[ derive(FromForm) ]
struct OrgIdData {
2018-10-10 20:40:39 +02:00
#[ form(field = " organizationId " ) ]
2018-12-30 23:34:31 +01:00
organization_id : String ,
2018-04-24 22:01:55 +02:00
}
2018-10-10 20:40:39 +02:00
#[ get( " /ciphers/organization-details?<data..> " ) ]
2021-03-27 16:07:26 +01:00
fn get_org_details ( data : Form < OrgIdData > , headers : Headers , conn : DbConn ) -> Json < Value > {
2018-10-10 20:40:39 +02:00
let ciphers = Cipher ::find_by_org ( & data . organization_id , & conn ) ;
2021-04-06 22:54:42 +02:00
let ciphers_json : Vec < Value > =
ciphers . iter ( ) . map ( | c | c . to_json ( & headers . host , & headers . user . uuid , & conn ) ) . collect ( ) ;
2018-04-24 22:01:55 +02:00
2021-03-27 16:07:26 +01:00
Json ( json! ( {
2018-04-27 13:49:34 +02:00
" Data " : ciphers_json ,
" Object " : " list " ,
2018-10-01 18:02:58 +02:00
" ContinuationToken " : null ,
2021-03-27 16:07:26 +01:00
} ) )
2018-04-24 22:01:55 +02:00
}
#[ get( " /organizations/<org_id>/users " ) ]
2021-03-27 16:07:26 +01:00
fn get_org_users ( org_id : String , _headers : ManagerHeadersLoose , conn : DbConn ) -> Json < Value > {
2018-04-24 22:01:55 +02:00
let users = UserOrganization ::find_by_org ( & org_id , & conn ) ;
2018-04-25 00:34:40 +02:00
let users_json : Vec < Value > = users . iter ( ) . map ( | c | c . to_json_user_details ( & conn ) ) . collect ( ) ;
2018-04-24 22:01:55 +02:00
2021-03-27 16:07:26 +01:00
Json ( json! ( {
2018-04-24 22:01:55 +02:00
" Data " : users_json ,
2018-10-01 18:02:58 +02:00
" Object " : " list " ,
" ContinuationToken " : null ,
2021-03-27 16:07:26 +01:00
} ) )
2018-04-24 22:01:55 +02:00
}
2018-02-17 22:30:19 +01:00
2018-04-24 22:01:55 +02:00
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
2018-04-25 00:34:40 +02:00
struct CollectionData {
2018-06-13 14:25:50 +02:00
Id : String ,
ReadOnly : bool ,
2020-07-03 06:51:20 +02:00
HidePasswords : bool ,
2018-04-24 22:01:55 +02:00
}
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
struct InviteData {
2018-06-01 00:18:50 +02:00
Emails : Vec < String > ,
Type : NumberOrString ,
2019-02-08 19:12:08 +01:00
Collections : Option < Vec < CollectionData > > ,
2018-06-01 00:18:50 +02:00
AccessAll : Option < bool > ,
2018-04-24 22:01:55 +02:00
}
#[ post( " /organizations/<org_id>/users/invite " , data = " <data> " ) ]
2018-06-01 00:18:50 +02:00
fn send_invite ( org_id : String , data : JsonUpcase < InviteData > , headers : AdminHeaders , conn : DbConn ) -> EmptyResult {
let data : InviteData = data . into_inner ( ) . data ;
2018-04-24 22:01:55 +02:00
2018-06-11 15:44:37 +02:00
let new_type = match UserOrgType ::from_str ( & data . Type . into_string ( ) ) {
2018-04-25 00:34:40 +02:00
Some ( new_type ) = > new_type as i32 ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Invalid type " ) ,
2018-04-25 00:34:40 +02:00
} ;
2018-12-30 23:34:31 +01:00
if new_type ! = UserOrgType ::User & & headers . org_user_type ! = UserOrgType ::Owner {
2018-11-12 18:13:25 +01:00
err! ( " Only Owners can invite Managers, Admins or Owners " )
2018-04-24 23:04:10 +02:00
}
2018-04-24 22:01:55 +02:00
2018-09-10 15:51:40 +02:00
for email in data . Emails . iter ( ) {
2019-02-08 18:45:07 +01:00
let mut user_org_status = if CONFIG . mail_enabled ( ) {
UserOrgStatus ::Invited as i32
} else {
UserOrgStatus ::Accepted as i32 // Automatically mark user as accepted if no email invites
2018-12-19 05:16:03 +01:00
} ;
2018-09-10 15:51:40 +02:00
let user = match User ::find_by_mail ( & email , & conn ) {
2018-12-30 23:34:31 +01:00
None = > {
2019-01-25 18:23:51 +01:00
if ! CONFIG . invitations_allowed ( ) {
2020-04-09 10:51:05 +02:00
err! ( format! ( " User does not exist: {} " , email ) )
}
2020-05-24 23:00:26 +02:00
if ! CONFIG . is_email_domain_allowed ( & email ) {
2020-04-09 10:51:05 +02:00
err! ( " Email domain not eligible for invitations " )
2019-01-08 15:11:16 +01:00
}
2019-02-02 01:09:21 +01:00
if ! CONFIG . mail_enabled ( ) {
2019-02-22 20:25:50 +01:00
let invitation = Invitation ::new ( email . clone ( ) ) ;
2018-12-30 23:34:31 +01:00
invitation . save ( & conn ) ? ;
}
2019-01-08 15:11:16 +01:00
let mut user = User ::new ( email . clone ( ) ) ;
user . save ( & conn ) ? ;
user_org_status = UserOrgStatus ::Invited as i32 ;
user
2018-12-30 23:34:31 +01:00
}
Some ( user ) = > {
if UserOrganization ::find_by_user_and_org ( & user . uuid , & org_id , & conn ) . is_some ( ) {
err! ( format! ( " User already in organization: {} " , email ) )
} else {
user
}
2018-09-10 15:51:40 +02:00
}
} ;
2018-12-19 22:51:08 +01:00
let mut new_user = UserOrganization ::new ( user . uuid . clone ( ) , org_id . clone ( ) ) ;
let access_all = data . AccessAll . unwrap_or ( false ) ;
new_user . access_all = access_all ;
2019-05-20 21:24:29 +02:00
new_user . atype = new_type ;
2018-12-19 22:51:08 +01:00
new_user . status = user_org_status ;
// If no accessAll, add the collections received
if ! access_all {
2019-02-08 19:12:08 +01:00
for col in data . Collections . iter ( ) . flatten ( ) {
2018-12-19 22:51:08 +01:00
match Collection ::find_by_uuid_and_org ( & col . Id , & org_id , & conn ) {
None = > err! ( " Collection not found in Organization " ) ,
Some ( collection ) = > {
2021-03-31 22:18:35 +02:00
CollectionUser ::save ( & user . uuid , & collection . uuid , col . ReadOnly , col . HidePasswords , & conn ) ? ;
2018-05-04 19:25:50 +02:00
}
2018-04-24 22:01:55 +02:00
}
}
2018-10-12 16:20:10 +02:00
}
2018-12-15 03:56:00 +01:00
2018-12-19 22:51:08 +01:00
new_user . save ( & conn ) ? ;
2019-02-02 01:09:21 +01:00
if CONFIG . mail_enabled ( ) {
2018-12-15 03:56:00 +01:00
let org_name = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( org ) = > org . name ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Error looking up organization " ) ,
2018-12-15 03:56:00 +01:00
} ;
2019-01-08 15:11:16 +01:00
2019-01-06 05:03:49 +01:00
mail ::send_invite (
2019-01-08 15:11:16 +01:00
& email ,
& user . uuid ,
Some ( org_id . clone ( ) ) ,
Some ( new_user . uuid ) ,
& org_name ,
Some ( headers . user . email . clone ( ) ) ,
) ? ;
2018-12-15 03:56:00 +01:00
}
}
Ok ( ( ) )
}
2018-12-30 06:19:01 +01:00
#[ post( " /organizations/<org_id>/users/<user_org>/reinvite " ) ]
2019-01-03 04:20:39 +01:00
fn reinvite_user ( org_id : String , user_org : String , headers : AdminHeaders , conn : DbConn ) -> EmptyResult {
2019-01-25 18:23:51 +01:00
if ! CONFIG . invitations_allowed ( ) {
2018-12-30 05:24:38 +01:00
err! ( " Invitations are not allowed. " )
}
2019-02-02 01:09:21 +01:00
if ! CONFIG . mail_enabled ( ) {
2018-12-30 05:24:38 +01:00
err! ( " SMTP is not configured. " )
}
2018-12-30 06:19:01 +01:00
let user_org = match UserOrganization ::find_by_uuid ( & user_org , & conn ) {
Some ( user_org ) = > user_org ,
2019-01-07 15:29:57 +01:00
None = > err! ( " The user hasn't been invited to the organization. " ) ,
2018-12-30 06:19:01 +01:00
} ;
2019-01-07 15:29:57 +01:00
if user_org . status ! = UserOrgStatus ::Invited as i32 {
err! ( " The user is already accepted or confirmed to the organization " )
}
2018-12-30 06:19:01 +01:00
let user = match User ::find_by_uuid ( & user_org . user_uuid , & conn ) {
2018-12-30 05:24:38 +01:00
Some ( user ) = > user ,
None = > err! ( " User not found. " ) ,
} ;
2019-01-08 14:05:05 +01:00
2018-12-30 05:24:38 +01:00
let org_name = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( org ) = > org . name ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Error looking up organization. " ) ,
2018-12-30 05:24:38 +01:00
} ;
2019-02-02 01:09:21 +01:00
if CONFIG . mail_enabled ( ) {
2019-01-04 16:32:51 +01:00
mail ::send_invite (
2019-01-08 15:11:16 +01:00
& user . email ,
& user . uuid ,
Some ( org_id ) ,
Some ( user_org . uuid ) ,
& org_name ,
Some ( headers . user . email ) ,
2019-01-04 16:32:51 +01:00
) ? ;
2019-01-08 15:11:16 +01:00
} else {
2019-11-02 17:39:01 +01:00
let invitation = Invitation ::new ( user . email ) ;
2019-01-08 15:11:16 +01:00
invitation . save ( & conn ) ? ;
2018-12-30 05:24:38 +01:00
}
2018-12-30 23:34:31 +01:00
2018-12-30 05:24:38 +01:00
Ok ( ( ) )
}
2018-12-19 05:16:03 +01:00
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
struct AcceptData {
Token : String ,
}
2018-12-21 03:37:03 +01:00
#[ post( " /organizations/<_org_id>/users/<_org_user_id>/accept " , data = " <data> " ) ]
fn accept_invite ( _org_id : String , _org_user_id : String , data : JsonUpcase < AcceptData > , conn : DbConn ) -> EmptyResult {
2018-12-30 23:34:31 +01:00
// The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead
2018-12-19 05:16:03 +01:00
let data : AcceptData = data . into_inner ( ) . data ;
let token = & data . Token ;
2019-01-19 21:36:34 +01:00
let claims = decode_invite ( & token ) ? ;
2018-12-15 03:56:00 +01:00
2018-12-19 05:16:03 +01:00
match User ::find_by_mail ( & claims . email , & conn ) {
Some ( _ ) = > {
2018-12-23 21:15:44 +01:00
Invitation ::take ( & claims . email , & conn ) ;
2019-01-08 15:11:16 +01:00
if let ( Some ( user_org ) , Some ( org ) ) = ( & claims . user_org_id , & claims . org_id ) {
let mut user_org = match UserOrganization ::find_by_uuid_and_org ( user_org , org , & conn ) {
Some ( user_org ) = > user_org ,
None = > err! ( " Error accepting the invitation " ) ,
} ;
if user_org . status ! = UserOrgStatus ::Invited as i32 {
err! ( " User already accepted the invitation " )
2018-12-15 03:56:00 +01:00
}
2019-01-08 15:11:16 +01:00
user_org . status = UserOrgStatus ::Accepted as i32 ;
user_org . save ( & conn ) ? ;
2018-12-15 03:56:00 +01:00
}
2018-12-30 23:34:31 +01:00
}
None = > err! ( " Invited user not found " ) ,
2018-04-24 22:01:55 +02:00
}
2019-02-02 01:09:21 +01:00
if CONFIG . mail_enabled ( ) {
2021-03-04 08:03:55 +01:00
let mut org_name = CONFIG . invitation_org_name ( ) ;
2019-01-06 05:03:49 +01:00
if let Some ( org_id ) = & claims . org_id {
org_name = match Organization ::find_by_uuid ( & org_id , & conn ) {
2019-01-04 16:32:51 +01:00
Some ( org ) = > org . name ,
2019-01-08 15:11:16 +01:00
None = > err! ( " Organization not found. " ) ,
2019-01-06 05:03:49 +01:00
} ;
2019-01-03 04:20:39 +01:00
} ;
2019-01-05 19:46:45 +01:00
if let Some ( invited_by_email ) = & claims . invited_by_email {
2019-01-04 16:32:51 +01:00
// User was invited to an organization, so they must be confirmed manually after acceptance
2019-02-02 01:09:21 +01:00
mail ::send_invite_accepted ( & claims . email , invited_by_email , & org_name ) ? ;
2019-01-04 16:32:51 +01:00
} else {
// User was invited from /admin, so they are automatically confirmed
2019-02-02 01:09:21 +01:00
mail ::send_invite_confirmed ( & claims . email , & org_name ) ? ;
2019-01-04 16:32:51 +01:00
}
2019-01-03 04:20:39 +01:00
}
2018-04-24 22:01:55 +02:00
Ok ( ( ) )
}
2018-09-04 12:24:53 +02:00
#[ post( " /organizations/<org_id>/users/<org_user_id>/confirm " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn confirm_invite (
org_id : String ,
org_user_id : String ,
data : JsonUpcase < Value > ,
headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-06-01 00:18:50 +02:00
let data = data . into_inner ( ) . data ;
2018-09-04 14:37:44 +02:00
let mut user_to_confirm = match UserOrganization ::find_by_uuid_and_org ( & org_user_id , & org_id , & conn ) {
2018-04-25 00:34:40 +02:00
Some ( user ) = > user ,
2018-12-30 23:34:31 +01:00
None = > err! ( " The specified user isn't a member of the organization " ) ,
2018-04-25 00:34:40 +02:00
} ;
2019-05-20 21:24:29 +02:00
if user_to_confirm . atype ! = UserOrgType ::User & & headers . org_user_type ! = UserOrgType ::Owner {
2018-11-12 18:13:25 +01:00
err! ( " Only Owners can confirm Managers, Admins or Owners " )
2018-04-24 23:04:10 +02:00
}
2018-04-24 22:01:55 +02:00
2018-04-25 00:34:40 +02:00
if user_to_confirm . status ! = UserOrgStatus ::Accepted as i32 {
2018-04-24 22:01:55 +02:00
err! ( " User in invalid state " )
}
2018-04-25 00:34:40 +02:00
user_to_confirm . status = UserOrgStatus ::Confirmed as i32 ;
2019-05-20 21:24:29 +02:00
user_to_confirm . akey = match data [ " Key " ] . as_str ( ) {
2018-04-24 22:01:55 +02:00
Some ( key ) = > key . to_string ( ) ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Invalid key provided " ) ,
2018-04-24 22:01:55 +02:00
} ;
2019-02-02 01:09:21 +01:00
if CONFIG . mail_enabled ( ) {
2019-01-03 04:20:39 +01:00
let org_name = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( org ) = > org . name ,
None = > err! ( " Error looking up organization. " ) ,
} ;
let address = match User ::find_by_uuid ( & user_to_confirm . user_uuid , & conn ) {
Some ( user ) = > user . email ,
None = > err! ( " Error looking up user. " ) ,
} ;
2019-02-02 01:09:21 +01:00
mail ::send_invite_confirmed ( & address , & org_name ) ? ;
2019-01-03 04:20:39 +01:00
}
2018-12-19 21:52:53 +01:00
user_to_confirm . save ( & conn )
2018-04-25 00:34:40 +02:00
}
2018-09-04 12:24:53 +02:00
#[ get( " /organizations/<org_id>/users/<org_user_id> " ) ]
fn get_user ( org_id : String , org_user_id : String , _headers : AdminHeaders , conn : DbConn ) -> JsonResult {
2018-09-04 14:37:44 +02:00
let user = match UserOrganization ::find_by_uuid_and_org ( & org_user_id , & org_id , & conn ) {
2018-04-25 00:34:40 +02:00
Some ( user ) = > user ,
2018-12-30 23:34:31 +01:00
None = > err! ( " The specified user isn't a member of the organization " ) ,
2018-04-25 00:34:40 +02:00
} ;
2018-05-11 20:08:02 +02:00
Ok ( Json ( user . to_json_details ( & conn ) ) )
2018-04-25 00:34:40 +02:00
}
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
struct EditUserData {
2018-06-01 00:18:50 +02:00
Type : NumberOrString ,
2019-02-08 19:12:08 +01:00
Collections : Option < Vec < CollectionData > > ,
2018-06-01 00:18:50 +02:00
AccessAll : bool ,
2018-04-25 00:34:40 +02:00
}
2018-09-04 12:24:53 +02:00
#[ put( " /organizations/<org_id>/users/<org_user_id> " , data = " <data> " , rank = 1) ]
2018-12-30 23:34:31 +01:00
fn put_organization_user (
org_id : String ,
org_user_id : String ,
data : JsonUpcase < EditUserData > ,
headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-09-04 12:24:53 +02:00
edit_user ( org_id , org_user_id , data , headers , conn )
2018-08-13 17:45:30 +02:00
}
2018-09-04 12:24:53 +02:00
#[ post( " /organizations/<org_id>/users/<org_user_id> " , data = " <data> " , rank = 1) ]
2018-12-30 23:34:31 +01:00
fn edit_user (
org_id : String ,
org_user_id : String ,
data : JsonUpcase < EditUserData > ,
headers : AdminHeaders ,
conn : DbConn ,
) -> EmptyResult {
2018-06-01 00:18:50 +02:00
let data : EditUserData = data . into_inner ( ) . data ;
2018-04-25 00:34:40 +02:00
2018-06-11 15:44:37 +02:00
let new_type = match UserOrgType ::from_str ( & data . Type . into_string ( ) ) {
2018-11-12 18:13:25 +01:00
Some ( new_type ) = > new_type ,
2018-12-30 23:34:31 +01:00
None = > err! ( " Invalid type " ) ,
2018-04-25 00:34:40 +02:00
} ;
2018-09-04 12:24:53 +02:00
let mut user_to_edit = match UserOrganization ::find_by_uuid_and_org ( & org_user_id , & org_id , & conn ) {
2018-04-25 00:34:40 +02:00
Some ( user ) = > user ,
2018-12-30 23:34:31 +01:00
None = > err! ( " The specified user isn't member of the organization " ) ,
2018-04-25 00:34:40 +02:00
} ;
2019-05-20 21:24:29 +02:00
if new_type ! = user_to_edit . atype
& & ( user_to_edit . atype > = UserOrgType ::Admin | | new_type > = UserOrgType ::Admin )
2018-12-30 23:34:31 +01:00
& & headers . org_user_type ! = UserOrgType ::Owner
{
2018-09-04 12:24:53 +02:00
err! ( " Only Owners can grant and remove Admin or Owner privileges " )
2018-04-25 00:34:40 +02:00
}
2019-05-20 21:24:29 +02:00
if user_to_edit . atype = = UserOrgType ::Owner & & headers . org_user_type ! = UserOrgType ::Owner {
2018-09-04 12:24:53 +02:00
err! ( " Only Owners can edit Owner users " )
2018-04-25 00:34:40 +02:00
}
2019-05-20 21:24:29 +02:00
if user_to_edit . atype = = UserOrgType ::Owner & & new_type ! = UserOrgType ::Owner {
2018-04-25 00:34:40 +02:00
// Removing owner permmission, check that there are at least another owner
2018-12-30 23:34:31 +01:00
let num_owners = UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::Owner as i32 , & conn ) . len ( ) ;
2018-04-25 00:34:40 +02:00
if num_owners < = 1 {
err! ( " Can't delete the last owner " )
}
}
2018-06-01 00:18:50 +02:00
user_to_edit . access_all = data . AccessAll ;
2019-05-20 21:24:29 +02:00
user_to_edit . atype = new_type as i32 ;
2018-04-25 00:34:40 +02:00
2018-05-04 19:25:50 +02:00
// Delete all the odd collections
2018-05-29 17:01:38 +02:00
for c in CollectionUser ::find_by_organization_and_user_uuid ( & org_id , & user_to_edit . user_uuid , & conn ) {
2018-12-19 21:52:53 +01:00
c . delete ( & conn ) ? ;
2018-05-04 19:25:50 +02:00
}
// If no accessAll, add the collections received
2018-06-01 00:18:50 +02:00
if ! data . AccessAll {
2019-02-08 19:12:08 +01:00
for col in data . Collections . iter ( ) . flatten ( ) {
2018-06-13 14:25:50 +02:00
match Collection ::find_by_uuid_and_org ( & col . Id , & org_id , & conn ) {
2018-05-28 18:26:02 +02:00
None = > err! ( " Collection not found in Organization " ) ,
Some ( collection ) = > {
2021-03-31 22:18:35 +02:00
CollectionUser ::save (
& user_to_edit . user_uuid ,
& collection . uuid ,
col . ReadOnly ,
col . HidePasswords ,
& conn ,
) ? ;
2018-05-28 18:26:02 +02:00
}
}
2018-05-04 19:25:50 +02:00
}
2018-04-25 00:34:40 +02:00
}
2018-12-19 21:52:53 +01:00
user_to_edit . save ( & conn )
2018-04-24 22:01:55 +02:00
}
2018-09-04 12:24:53 +02:00
#[ delete( " /organizations/<org_id>/users/<org_user_id> " ) ]
fn delete_user ( org_id : String , org_user_id : String , headers : AdminHeaders , conn : DbConn ) -> EmptyResult {
2018-09-04 14:37:44 +02:00
let user_to_delete = match UserOrganization ::find_by_uuid_and_org ( & org_user_id , & org_id , & conn ) {
2018-04-25 00:34:40 +02:00
Some ( user ) = > user ,
2018-12-30 23:34:31 +01:00
None = > err! ( " User to delete isn't member of the organization " ) ,
2018-04-25 00:34:40 +02:00
} ;
2019-05-20 21:24:29 +02:00
if user_to_delete . atype ! = UserOrgType ::User & & headers . org_user_type ! = UserOrgType ::Owner {
2018-04-24 23:04:10 +02:00
err! ( " Only Owners can delete Admins or Owners " )
}
2018-02-17 22:30:19 +01:00
2019-05-20 21:24:29 +02:00
if user_to_delete . atype = = UserOrgType ::Owner {
2018-04-25 00:34:40 +02:00
// Removing owner, check that there are at least another owner
2018-12-30 23:34:31 +01:00
let num_owners = UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::Owner as i32 , & conn ) . len ( ) ;
2018-04-25 00:34:40 +02:00
if num_owners < = 1 {
err! ( " Can't delete the last owner " )
}
}
2018-02-17 22:30:19 +01:00
2018-12-19 21:52:53 +01:00
user_to_delete . delete ( & conn )
2018-08-13 17:45:30 +02:00
}
2018-09-04 12:24:53 +02:00
#[ post( " /organizations/<org_id>/users/<org_user_id>/delete " ) ]
fn post_delete_user ( org_id : String , org_user_id : String , headers : AdminHeaders , conn : DbConn ) -> EmptyResult {
delete_user ( org_id , org_user_id , headers , conn )
2018-09-13 15:16:24 +02:00
}
use super ::ciphers ::update_cipher_from_data ;
2018-12-30 23:34:31 +01:00
use super ::ciphers ::CipherData ;
2018-09-13 15:16:24 +02:00
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
struct ImportData {
Ciphers : Vec < CipherData > ,
Collections : Vec < NewCollectionData > ,
CollectionRelationships : Vec < RelationsData > ,
}
#[ derive(Deserialize) ]
#[ allow(non_snake_case) ]
struct RelationsData {
// Cipher index
Key : usize ,
// Collection index
Value : usize ,
}
2018-10-10 20:40:39 +02:00
#[ post( " /ciphers/import-organization?<query..> " , data = " <data> " ) ]
2018-12-30 23:34:31 +01:00
fn post_org_import (
query : Form < OrgIdData > ,
data : JsonUpcase < ImportData > ,
2020-03-14 13:22:30 +01:00
headers : AdminHeaders ,
2018-12-30 23:34:31 +01:00
conn : DbConn ,
nt : Notify ,
) -> EmptyResult {
2018-09-13 15:16:24 +02:00
let data : ImportData = data . into_inner ( ) . data ;
2018-10-10 20:40:39 +02:00
let org_id = query . into_inner ( ) . organization_id ;
2018-09-13 15:16:24 +02:00
// Read and create the collections
2018-12-30 23:34:31 +01:00
let collections : Vec < _ > = data
. Collections
. into_iter ( )
. map ( | coll | {
2019-02-22 20:25:50 +01:00
let collection = Collection ::new ( org_id . clone ( ) , coll . Name ) ;
2018-12-30 23:34:31 +01:00
if collection . save ( & conn ) . is_err ( ) {
err! ( " Failed to create Collection " ) ;
}
Ok ( collection )
} )
. collect ( ) ;
2018-09-13 15:16:24 +02:00
// Read the relations between collections and ciphers
let mut relations = Vec ::new ( ) ;
for relation in data . CollectionRelationships {
relations . push ( ( relation . Key , relation . Value ) ) ;
}
2020-03-14 13:22:30 +01:00
let headers : Headers = headers . into ( ) ;
2018-09-13 15:16:24 +02:00
// Read and create the ciphers
2018-12-30 23:34:31 +01:00
let ciphers : Vec < _ > = data
. Ciphers
. into_iter ( )
. map ( | cipher_data | {
let mut cipher = Cipher ::new ( cipher_data . Type , cipher_data . Name . clone ( ) ) ;
2021-04-06 22:54:42 +02:00
update_cipher_from_data ( & mut cipher , cipher_data , & headers , false , & conn , & nt , UpdateType ::CipherCreate )
. ok ( ) ;
2018-12-30 23:34:31 +01:00
cipher
} )
. collect ( ) ;
2018-09-13 15:16:24 +02:00
// Assign the collections
for ( cipher_index , coll_index ) in relations {
let cipher_id = & ciphers [ cipher_index ] . uuid ;
2018-10-01 18:50:31 +02:00
let coll = & collections [ coll_index ] ;
let coll_id = match coll {
Ok ( coll ) = > coll . uuid . as_str ( ) ,
2018-12-30 23:34:31 +01:00
Err ( _ ) = > err! ( " Failed to assign to collection " ) ,
2018-10-01 18:50:31 +02:00
} ;
2018-12-30 23:34:31 +01:00
2018-12-19 21:52:53 +01:00
CollectionCipher ::save ( cipher_id , coll_id , & conn ) ? ;
2018-09-13 15:16:24 +02:00
}
let mut user = headers . user ;
2018-12-19 21:52:53 +01:00
user . update_revision ( & conn )
2018-10-10 20:40:39 +02:00
}
2020-03-14 13:22:30 +01:00
#[ get( " /organizations/<org_id>/policies " ) ]
2021-03-27 16:07:26 +01:00
fn list_policies ( org_id : String , _headers : AdminHeaders , conn : DbConn ) -> Json < Value > {
2020-03-14 13:22:30 +01:00
let policies = OrgPolicy ::find_by_org ( & org_id , & conn ) ;
let policies_json : Vec < Value > = policies . iter ( ) . map ( OrgPolicy ::to_json ) . collect ( ) ;
2021-03-27 16:07:26 +01:00
Json ( json! ( {
2020-03-14 13:22:30 +01:00
" Data " : policies_json ,
" Object " : " list " ,
" ContinuationToken " : null
2021-03-27 16:07:26 +01:00
} ) )
2020-03-14 13:22:30 +01:00
}
2020-03-20 10:51:17 +01:00
#[ get( " /organizations/<org_id>/policies/token?<token> " ) ]
fn list_policies_token ( org_id : String , token : String , conn : DbConn ) -> JsonResult {
let invite = crate ::auth ::decode_invite ( & token ) ? ;
let invite_org_id = match invite . org_id {
Some ( invite_org_id ) = > invite_org_id ,
None = > err! ( " Invalid token " ) ,
} ;
if invite_org_id ! = org_id {
err! ( " Token doesn't match request organization " ) ;
}
2020-07-14 18:00:09 +02:00
2020-03-20 10:51:17 +01:00
// TODO: We receive the invite token as ?token=<>, validate it contains the org id
let policies = OrgPolicy ::find_by_org ( & org_id , & conn ) ;
let policies_json : Vec < Value > = policies . iter ( ) . map ( OrgPolicy ::to_json ) . collect ( ) ;
Ok ( Json ( json! ( {
" Data " : policies_json ,
" Object " : " list " ,
" ContinuationToken " : null
} ) ) )
}
2020-03-14 13:22:30 +01:00
#[ get( " /organizations/<org_id>/policies/<pol_type> " ) ]
fn get_policy ( org_id : String , pol_type : i32 , _headers : AdminHeaders , conn : DbConn ) -> JsonResult {
let pol_type_enum = match OrgPolicyType ::from_i32 ( pol_type ) {
Some ( pt ) = > pt ,
2021-01-24 05:50:06 +01:00
None = > err! ( " Invalid or unsupported policy type " ) ,
2020-03-14 13:22:30 +01:00
} ;
let policy = match OrgPolicy ::find_by_org_and_type ( & org_id , pol_type , & conn ) {
Some ( p ) = > p ,
None = > OrgPolicy ::new ( org_id , pol_type_enum , " {} " . to_string ( ) ) ,
} ;
Ok ( Json ( policy . to_json ( ) ) )
}
#[ derive(Deserialize) ]
struct PolicyData {
enabled : bool ,
#[ serde(rename = " type " ) ]
_type : i32 ,
data : Value ,
}
#[ put( " /organizations/<org_id>/policies/<pol_type> " , data = " <data> " ) ]
2021-03-31 22:18:35 +02:00
fn put_policy (
org_id : String ,
pol_type : i32 ,
data : Json < PolicyData > ,
_headers : AdminHeaders ,
conn : DbConn ,
) -> JsonResult {
2020-03-14 13:22:30 +01:00
let data : PolicyData = data . into_inner ( ) ;
let pol_type_enum = match OrgPolicyType ::from_i32 ( pol_type ) {
Some ( pt ) = > pt ,
None = > err! ( " Invalid policy type " ) ,
} ;
let mut policy = match OrgPolicy ::find_by_org_and_type ( & org_id , pol_type , & conn ) {
Some ( p ) = > p ,
None = > OrgPolicy ::new ( org_id , pol_type_enum , " {} " . to_string ( ) ) ,
} ;
policy . enabled = data . enabled ;
policy . data = serde_json ::to_string ( & data . data ) ? ;
policy . save ( & conn ) ? ;
Ok ( Json ( policy . to_json ( ) ) )
2020-04-09 10:51:05 +02:00
}
2020-09-14 08:34:17 +02:00
2021-01-31 21:46:37 +01:00
#[ allow(unused_variables) ]
#[ get( " /organizations/<org_id>/tax " ) ]
fn get_organization_tax ( org_id : String , _headers : Headers , _conn : DbConn ) -> EmptyResult {
// Prevent a 404 error, which also causes Javascript errors.
err! ( " Only allowed when not self hosted. " )
}
2020-09-14 08:34:17 +02:00
#[ get( " /plans " ) ]
2021-03-27 16:07:26 +01:00
fn get_plans ( _headers : Headers , _conn : DbConn ) -> Json < Value > {
Json ( json! ( {
2020-09-14 08:34:17 +02:00
" Object " : " list " ,
" Data " : [
{
" Object " : " plan " ,
" Type " : 0 ,
" Product " : 0 ,
" Name " : " Free " ,
" IsAnnual " : false ,
" NameLocalizationKey " : " planNameFree " ,
" DescriptionLocalizationKey " : " planDescFree " ,
" CanBeUsedByBusiness " : false ,
" BaseSeats " : 2 ,
" BaseStorageGb " : null ,
" MaxCollections " : 2 ,
" MaxUsers " : 2 ,
" HasAdditionalSeatsOption " : false ,
" MaxAdditionalSeats " : null ,
" HasAdditionalStorageOption " : false ,
" MaxAdditionalStorage " : null ,
" HasPremiumAccessOption " : false ,
" TrialPeriodDays " : null ,
" HasSelfHost " : false ,
" HasPolicies " : false ,
" HasGroups " : false ,
" HasDirectory " : false ,
" HasEvents " : false ,
" HasTotp " : false ,
" Has2fa " : false ,
" HasApi " : false ,
" HasSso " : false ,
" UsersGetPremium " : false ,
" UpgradeSortOrder " : - 1 ,
" DisplaySortOrder " : - 1 ,
" LegacyYear " : null ,
" Disabled " : false ,
" StripePlanId " : null ,
" StripeSeatPlanId " : null ,
" StripeStoragePlanId " : null ,
" StripePremiumAccessPlanId " : null ,
" BasePrice " : 0.0 ,
" SeatPrice " : 0.0 ,
" AdditionalStoragePricePerGb " : 0.0 ,
" PremiumAccessOptionPrice " : 0.0
}
] ,
" ContinuationToken " : null
2021-03-27 16:07:26 +01:00
} ) )
2021-01-24 05:50:06 +01:00
}
2021-01-31 21:46:37 +01:00
#[ get( " /plans/sales-tax-rates " ) ]
2021-03-27 16:07:26 +01:00
fn get_plans_tax_rates ( _headers : Headers , _conn : DbConn ) -> Json < Value > {
2021-01-31 21:46:37 +01:00
// Prevent a 404 error, which also causes Javascript errors.
2021-03-27 16:07:26 +01:00
Json ( json! ( {
2021-01-31 21:46:37 +01:00
" Object " : " list " ,
" Data " : [ ] ,
" ContinuationToken " : null
2021-03-27 16:07:26 +01:00
} ) )
2021-01-31 21:46:37 +01:00
}
2021-02-06 18:22:39 +01:00
#[ derive(Deserialize, Debug) ]
#[ allow(non_snake_case) ]
struct OrgImportGroupData {
Name : String , // "GroupName"
ExternalId : String , // "cn=GroupName,ou=Groups,dc=example,dc=com"
Users : Vec < String > , // ["uid=user,ou=People,dc=example,dc=com"]
}
#[ derive(Deserialize, Debug) ]
#[ allow(non_snake_case) ]
struct OrgImportUserData {
Email : String , // "user@maildomain.net"
ExternalId : String , // "uid=user,ou=People,dc=example,dc=com"
Deleted : bool ,
}
#[ derive(Deserialize, Debug) ]
#[ allow(non_snake_case) ]
struct OrgImportData {
Groups : Vec < OrgImportGroupData > ,
OverwriteExisting : bool ,
Users : Vec < OrgImportUserData > ,
}
#[ post( " /organizations/<org_id>/import " , data = " <data> " ) ]
fn import ( org_id : String , data : JsonUpcase < OrgImportData > , headers : Headers , conn : DbConn ) -> EmptyResult {
let data = data . into_inner ( ) . data ;
// TODO: Currently we aren't storing the externalId's anywhere, so we also don't have a way
// to differentiate between auto-imported users and manually added ones.
// This means that this endpoint can end up removing users that were added manually by an admin,
// as opposed to upstream which only removes auto-imported users.
// User needs to be admin or owner to use the Directry Connector
match UserOrganization ::find_by_user_and_org ( & headers . user . uuid , & org_id , & conn ) {
Some ( user_org ) if user_org . atype > = UserOrgType ::Admin = > { /* Okay, nothing to do */ }
Some ( _ ) = > err! ( " User has insufficient permissions to use Directory Connector " ) ,
None = > err! ( " User not part of organization " ) ,
} ;
for user_data in & data . Users {
if user_data . Deleted {
// If user is marked for deletion and it exists, delete it
if let Some ( user_org ) = UserOrganization ::find_by_email_and_org ( & user_data . Email , & org_id , & conn ) {
user_org . delete ( & conn ) ? ;
}
// 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 ( ) {
2021-03-31 22:18:35 +02:00
if let Some ( user ) = User ::find_by_mail ( & user_data . Email , & conn ) {
2021-02-06 18:22:39 +01:00
let user_org_status = if CONFIG . mail_enabled ( ) {
UserOrgStatus ::Invited as i32
} else {
UserOrgStatus ::Accepted as i32 // Automatically mark user as accepted if no email invites
} ;
let mut new_org_user = UserOrganization ::new ( user . uuid . clone ( ) , org_id . clone ( ) ) ;
new_org_user . access_all = false ;
new_org_user . atype = UserOrgType ::User as i32 ;
new_org_user . status = user_org_status ;
new_org_user . save ( & conn ) ? ;
if CONFIG . mail_enabled ( ) {
let org_name = match Organization ::find_by_uuid ( & org_id , & conn ) {
Some ( org ) = > org . name ,
None = > err! ( " Error looking up organization " ) ,
} ;
mail ::send_invite (
& user_data . Email ,
& user . uuid ,
Some ( org_id . clone ( ) ) ,
Some ( new_org_user . uuid ) ,
& org_name ,
Some ( headers . user . email . clone ( ) ) ,
) ? ;
}
2021-03-27 16:07:26 +01:00
}
2021-02-06 18:22:39 +01:00
}
}
// 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 {
2021-03-27 16:07:26 +01:00
for user_org in UserOrganization ::find_by_org_and_type ( & org_id , UserOrgType ::User as i32 , & conn ) {
2021-03-31 22:18:35 +02:00
if let Some ( user_email ) = User ::find_by_uuid ( & user_org . user_uuid , & conn ) . map ( | u | u . email ) {
2021-02-06 18:22:39 +01:00
if ! data . Users . iter ( ) . any ( | u | u . Email = = user_email ) {
user_org . delete ( & conn ) ? ;
}
2021-03-27 16:07:26 +01:00
}
2021-02-06 18:22:39 +01:00
}
}
Ok ( ( ) )
}