Skip to main content

haste_server/
tenants.rs

1use crate::{
2    auth_n::oidc::utilities::set_user_password, fhir_client::ServerCTX, services::ServerState,
3    ui::components::TenantName,
4};
5use haste_fhir_client::FHIRClient;
6use haste_fhir_model::r4::generated::{
7    resources::{Project, Resource, ResourceType, User},
8    terminology::{IssueType, SupportedFhirVersion},
9    types::FHIRString,
10};
11use haste_fhir_operation_error::OperationOutcomeError;
12use haste_fhir_search::SearchEngine;
13use haste_fhir_terminology::FHIRTerminology;
14use haste_jwt::{ProjectId, TenantId, claims::SubscriptionTier};
15use haste_repository::{
16    Repository,
17    admin::TenantModelAdmin,
18    types::{
19        tenant::{CreateTenant, Tenant},
20        user::CreateUser,
21    },
22    utilities::generate_id,
23};
24use std::sync::Arc;
25
26pub async fn create_user<
27    Repo: Repository + Send + Sync + 'static,
28    Search: SearchEngine + Send + Sync + 'static,
29    Terminology: FHIRTerminology + Send + Sync + 'static,
30>(
31    services: &ServerState<Repo, Search, Terminology>,
32    tenant: &TenantId,
33    user_resource: User,
34    password: Option<&str>,
35) -> Result<User, OperationOutcomeError> {
36    let ctx = Arc::new(ServerCTX::system(
37        tenant.clone(),
38        ProjectId::System,
39        services.fhir_client.clone(),
40        services.rate_limit.clone(),
41    ));
42
43    let user = services
44        .fhir_client
45        .create(ctx, ResourceType::User, Resource::User(user_resource))
46        .await?;
47
48    let user = match user {
49        Resource::User(user) => user,
50        _ => panic!("Created resource is not a User"),
51    };
52
53    let user_id = user.id.clone().unwrap();
54
55    if let Some(password) = password {
56        set_user_password(
57            &*services.repo,
58            tenant,
59            &user
60                .email
61                .as_ref()
62                .and_then(|e| e.value.as_ref())
63                .map(|s| s.to_string())
64                .unwrap_or_default(),
65            &user_id,
66            password,
67        )
68        .await?;
69    }
70
71    Ok(user)
72}
73
74pub struct CreateTenantOutput {
75    pub tenant: Tenant,
76    pub owner: haste_repository::types::user::User,
77}
78
79pub async fn read_tenant<
80    Repo: Repository + Send + Sync + 'static,
81    Search: SearchEngine + Send + Sync + 'static,
82    Terminology: FHIRTerminology + Send + Sync + 'static,
83>(
84    services: &ServerState<Repo, Search, Terminology>,
85    tenant_id: &TenantId,
86) -> Result<Tenant, OperationOutcomeError> {
87    TenantModelAdmin::<CreateTenant, _, _, _, _>::read(
88        services.repo.as_ref(),
89        &TenantId::System,
90        &tenant_id.as_ref().to_string(),
91    )
92    .await?
93    .ok_or_else(|| {
94        OperationOutcomeError::error(
95            IssueType::not_found(),
96            format!("Tenant '{}' was not found.", tenant_id),
97        )
98    })
99}
100
101pub fn tenant_name(tenant: &Tenant) -> TenantName {
102    TenantName(tenant.display_name.clone())
103}
104
105pub async fn create_tenant<
106    Repo: Repository + Send + Sync + 'static,
107    Search: SearchEngine + Send + Sync + 'static,
108    Terminology: FHIRTerminology + Send + Sync + 'static,
109>(
110    services: &ServerState<Repo, Search, Terminology>,
111    tenant_id: Option<String>,
112    _name: &str,
113    subscription_tier: &SubscriptionTier,
114    owner: haste_fhir_model::r4::generated::resources::User,
115    owner_password: Option<&str>,
116) -> Result<CreateTenantOutput, OperationOutcomeError> {
117    let services = services.transaction().await?;
118
119    let new_tenant = TenantModelAdmin::create(
120        &*services.repo,
121        &TenantId::System,
122        CreateTenant {
123            id: Some(TenantId::new(tenant_id.unwrap_or(generate_id(Some(16))))),
124            subscription_tier: Some(subscription_tier.clone().into()),
125            display_name: None,
126            logo_data: None,
127            logo_content_type: None,
128        },
129    )
130    .await?;
131
132    services
133        .fhir_client
134        .create(
135            Arc::new(ServerCTX::system(
136                new_tenant.id.clone(),
137                ProjectId::System,
138                services.fhir_client.clone(),
139                services.rate_limit.clone(),
140            )),
141            ResourceType::Project,
142            Resource::Project(Project {
143                id: Some(ProjectId::System.to_string()),
144                name: Box::new(FHIRString {
145                    value: Some(ProjectId::System.to_string()),
146                    ..Default::default()
147                }),
148                fhirVersion: SupportedFhirVersion::r4(),
149                ..Default::default()
150            }),
151        )
152        .await?;
153
154    let user = create_user(&services, &new_tenant.id, owner, owner_password).await?;
155
156    let Some(user_id) = user.id else {
157        return Err(OperationOutcomeError::fatal(
158            IssueType::invalid(),
159            "The user ID is required to complete the tenant creation process.".to_string(),
160        ));
161    };
162
163    let Some(user) = TenantModelAdmin::<CreateUser, _, _, _, _>::read(
164        services.repo.as_ref(),
165        &new_tenant.id,
166        &user_id,
167    )
168    .await?
169    else {
170        return Err(OperationOutcomeError::fatal(
171            IssueType::invalid(),
172            "The user does not exist after creation.".to_string(),
173        ));
174    };
175
176    services.commit().await?;
177
178    Ok(CreateTenantOutput {
179        tenant: new_tenant,
180        owner: user,
181    })
182}