1use std::sync::Arc;
9
10use async_trait::async_trait;
11use http::StatusCode;
12use serde_json::{json, Value};
13use tokio::sync::Mutex as AsyncMutex;
14use uuid::Uuid;
15
16use fakecloud_core::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
17use fakecloud_persistence::SnapshotStore;
18
19use crate::persistence::save_snapshot;
20use crate::state::{
21 SharedSsoAdminState, StoredApplication, StoredAssignment, StoredInstance, StoredManagedPolicy,
22 StoredOpStatus, StoredPermissionSet, StoredProvisioningStatus, StoredRegion,
23 StoredTrustedTokenIssuer,
24};
25
26pub const SSOADMIN_ACTIONS: &[&str] = &[
28 "AddRegion",
29 "AttachCustomerManagedPolicyReferenceToPermissionSet",
30 "AttachManagedPolicyToPermissionSet",
31 "CreateAccountAssignment",
32 "CreateApplication",
33 "CreateApplicationAssignment",
34 "CreateInstance",
35 "CreateInstanceAccessControlAttributeConfiguration",
36 "CreatePermissionSet",
37 "CreateTrustedTokenIssuer",
38 "DeleteAccountAssignment",
39 "DeleteApplication",
40 "DeleteApplicationAccessScope",
41 "DeleteApplicationAssignment",
42 "DeleteApplicationAuthenticationMethod",
43 "DeleteApplicationGrant",
44 "DeleteInlinePolicyFromPermissionSet",
45 "DeleteInstance",
46 "DeleteInstanceAccessControlAttributeConfiguration",
47 "DeletePermissionSet",
48 "DeletePermissionsBoundaryFromPermissionSet",
49 "DeleteTrustedTokenIssuer",
50 "DescribeAccountAssignmentCreationStatus",
51 "DescribeAccountAssignmentDeletionStatus",
52 "DescribeApplication",
53 "DescribeApplicationAssignment",
54 "DescribeApplicationProvider",
55 "DescribeInstance",
56 "DescribeInstanceAccessControlAttributeConfiguration",
57 "DescribePermissionSet",
58 "DescribePermissionSetProvisioningStatus",
59 "DescribeRegion",
60 "DescribeTrustedTokenIssuer",
61 "DetachCustomerManagedPolicyReferenceFromPermissionSet",
62 "DetachManagedPolicyFromPermissionSet",
63 "GetApplicationAccessScope",
64 "GetApplicationAssignmentConfiguration",
65 "GetApplicationAuthenticationMethod",
66 "GetApplicationGrant",
67 "GetApplicationSessionConfiguration",
68 "GetInlinePolicyForPermissionSet",
69 "GetPermissionsBoundaryForPermissionSet",
70 "ListAccountAssignmentCreationStatus",
71 "ListAccountAssignmentDeletionStatus",
72 "ListAccountAssignments",
73 "ListAccountAssignmentsForPrincipal",
74 "ListAccountsForProvisionedPermissionSet",
75 "ListApplicationAccessScopes",
76 "ListApplicationAssignments",
77 "ListApplicationAssignmentsForPrincipal",
78 "ListApplicationAuthenticationMethods",
79 "ListApplicationGrants",
80 "ListApplicationProviders",
81 "ListApplications",
82 "ListCustomerManagedPolicyReferencesInPermissionSet",
83 "ListInstances",
84 "ListManagedPoliciesInPermissionSet",
85 "ListPermissionSetProvisioningStatus",
86 "ListPermissionSets",
87 "ListPermissionSetsProvisionedToAccount",
88 "ListRegions",
89 "ListTagsForResource",
90 "ListTrustedTokenIssuers",
91 "ProvisionPermissionSet",
92 "PutApplicationAccessScope",
93 "PutApplicationAssignmentConfiguration",
94 "PutApplicationAuthenticationMethod",
95 "PutApplicationGrant",
96 "PutApplicationSessionConfiguration",
97 "PutInlinePolicyToPermissionSet",
98 "PutPermissionsBoundaryToPermissionSet",
99 "RemoveRegion",
100 "TagResource",
101 "UntagResource",
102 "UpdateApplication",
103 "UpdateInstance",
104 "UpdateInstanceAccessControlAttributeConfiguration",
105 "UpdatePermissionSet",
106 "UpdateTrustedTokenIssuer",
107];
108
109const APPLICATION_PROVIDERS: &[(&str, &str, &str)] = &[
113 (
114 "arn:aws:sso::aws:applicationProvider/custom",
115 "OAUTH",
116 "Custom application",
117 ),
118 (
119 "arn:aws:sso::aws:applicationProvider/sso",
120 "SAML",
121 "IAM Identity Center",
122 ),
123 (
124 "arn:aws:sso::aws:applicationProvider/salesforce",
125 "SAML",
126 "Salesforce",
127 ),
128 ("arn:aws:sso::aws:applicationProvider/box", "SAML", "Box"),
129 (
130 "arn:aws:sso::aws:applicationProvider/slack",
131 "SAML",
132 "Slack",
133 ),
134 (
135 "arn:aws:sso::aws:applicationProvider/google",
136 "SAML",
137 "Google Workspace",
138 ),
139 (
140 "arn:aws:sso::aws:applicationProvider/microsoft365",
141 "SAML",
142 "Microsoft 365",
143 ),
144];
145
146pub struct SsoAdminService {
147 state: SharedSsoAdminState,
148 snapshot_store: Option<Arc<dyn SnapshotStore>>,
149 snapshot_lock: Arc<AsyncMutex<()>>,
150}
151
152impl SsoAdminService {
153 pub fn new(state: SharedSsoAdminState) -> Self {
154 Self {
155 state,
156 snapshot_store: None,
157 snapshot_lock: Arc::new(AsyncMutex::new(())),
158 }
159 }
160
161 pub fn with_snapshot_store(mut self, store: Arc<dyn SnapshotStore>) -> Self {
162 self.snapshot_store = Some(store);
163 self
164 }
165
166 async fn save(&self) {
167 save_snapshot(
168 &self.state,
169 self.snapshot_store.clone(),
170 &self.snapshot_lock,
171 )
172 .await;
173 }
174}
175
176#[async_trait]
177impl AwsService for SsoAdminService {
178 fn service_name(&self) -> &str {
179 "sso"
180 }
181
182 async fn handle(&self, request: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
183 let mutates = is_mutating(request.action.as_str());
184 let result = dispatch(self, &request);
185 if mutates && matches!(result.as_ref(), Ok(resp) if resp.status.is_success()) {
186 self.save().await;
187 }
188 result
189 }
190
191 fn supported_actions(&self) -> &[&str] {
192 SSOADMIN_ACTIONS
193 }
194}
195
196fn is_mutating(action: &str) -> bool {
197 const PREFIXES: &[&str] = &[
198 "Create",
199 "Update",
200 "Delete",
201 "Put",
202 "Attach",
203 "Detach",
204 "Add",
205 "Remove",
206 "Tag",
207 "Untag",
208 "Provision",
209 ];
210 PREFIXES.iter().any(|p| action.starts_with(p))
211}
212
213fn dispatch(s: &SsoAdminService, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
214 match req.action.as_str() {
215 "CreateInstance" => s.create_instance(req),
217 "DescribeInstance" => s.describe_instance(req),
218 "UpdateInstance" => s.update_instance(req),
219 "DeleteInstance" => s.delete_instance(req),
220 "ListInstances" => s.list_instances(req),
221 "CreatePermissionSet" => s.create_permission_set(req),
223 "DescribePermissionSet" => s.describe_permission_set(req),
224 "UpdatePermissionSet" => s.update_permission_set(req),
225 "DeletePermissionSet" => s.delete_permission_set(req),
226 "ListPermissionSets" => s.list_permission_sets(req),
227 "AttachManagedPolicyToPermissionSet" => s.attach_managed_policy(req),
229 "DetachManagedPolicyFromPermissionSet" => s.detach_managed_policy(req),
230 "ListManagedPoliciesInPermissionSet" => s.list_managed_policies(req),
231 "AttachCustomerManagedPolicyReferenceToPermissionSet" => s.attach_customer_policy(req),
232 "DetachCustomerManagedPolicyReferenceFromPermissionSet" => s.detach_customer_policy(req),
233 "ListCustomerManagedPolicyReferencesInPermissionSet" => s.list_customer_policies(req),
234 "PutInlinePolicyToPermissionSet" => s.put_inline_policy(req),
235 "GetInlinePolicyForPermissionSet" => s.get_inline_policy(req),
236 "DeleteInlinePolicyFromPermissionSet" => s.delete_inline_policy(req),
237 "PutPermissionsBoundaryToPermissionSet" => s.put_permissions_boundary(req),
238 "GetPermissionsBoundaryForPermissionSet" => s.get_permissions_boundary(req),
239 "DeletePermissionsBoundaryFromPermissionSet" => s.delete_permissions_boundary(req),
240 "CreateAccountAssignment" => s.create_account_assignment(req),
242 "DeleteAccountAssignment" => s.delete_account_assignment(req),
243 "DescribeAccountAssignmentCreationStatus" => s.describe_aa_creation_status(req),
244 "DescribeAccountAssignmentDeletionStatus" => s.describe_aa_deletion_status(req),
245 "ListAccountAssignmentCreationStatus" => s.list_aa_creation_status(req),
246 "ListAccountAssignmentDeletionStatus" => s.list_aa_deletion_status(req),
247 "ListAccountAssignments" => s.list_account_assignments(req),
248 "ListAccountAssignmentsForPrincipal" => s.list_account_assignments_for_principal(req),
249 "ListAccountsForProvisionedPermissionSet" => s.list_accounts_for_provisioned(req),
250 "ListPermissionSetsProvisionedToAccount" => s.list_ps_provisioned_to_account(req),
251 "ProvisionPermissionSet" => s.provision_permission_set(req),
253 "DescribePermissionSetProvisioningStatus" => s.describe_ps_provisioning_status(req),
254 "ListPermissionSetProvisioningStatus" => s.list_ps_provisioning_status(req),
255 "CreateApplication" => s.create_application(req),
257 "DescribeApplication" => s.describe_application(req),
258 "UpdateApplication" => s.update_application(req),
259 "DeleteApplication" => s.delete_application(req),
260 "ListApplications" => s.list_applications(req),
261 "CreateApplicationAssignment" => s.create_application_assignment(req),
263 "DeleteApplicationAssignment" => s.delete_application_assignment(req),
264 "DescribeApplicationAssignment" => s.describe_application_assignment(req),
265 "ListApplicationAssignments" => s.list_application_assignments(req),
266 "ListApplicationAssignmentsForPrincipal" => {
267 s.list_application_assignments_for_principal(req)
268 }
269 "GetApplicationAssignmentConfiguration" => s.get_application_assignment_configuration(req),
270 "PutApplicationAssignmentConfiguration" => s.put_application_assignment_configuration(req),
271 "PutApplicationAccessScope" => s.put_application_access_scope(req),
273 "GetApplicationAccessScope" => s.get_application_access_scope(req),
274 "DeleteApplicationAccessScope" => s.delete_application_access_scope(req),
275 "ListApplicationAccessScopes" => s.list_application_access_scopes(req),
276 "PutApplicationAuthenticationMethod" => s.put_application_authentication_method(req),
278 "GetApplicationAuthenticationMethod" => s.get_application_authentication_method(req),
279 "DeleteApplicationAuthenticationMethod" => s.delete_application_authentication_method(req),
280 "ListApplicationAuthenticationMethods" => s.list_application_authentication_methods(req),
281 "PutApplicationGrant" => s.put_application_grant(req),
283 "GetApplicationGrant" => s.get_application_grant(req),
284 "DeleteApplicationGrant" => s.delete_application_grant(req),
285 "ListApplicationGrants" => s.list_application_grants(req),
286 "GetApplicationSessionConfiguration" => s.get_application_session_configuration(req),
288 "PutApplicationSessionConfiguration" => s.put_application_session_configuration(req),
289 "ListApplicationProviders" => s.list_application_providers(req),
291 "DescribeApplicationProvider" => s.describe_application_provider(req),
292 "CreateTrustedTokenIssuer" => s.create_trusted_token_issuer(req),
294 "DescribeTrustedTokenIssuer" => s.describe_trusted_token_issuer(req),
295 "UpdateTrustedTokenIssuer" => s.update_trusted_token_issuer(req),
296 "DeleteTrustedTokenIssuer" => s.delete_trusted_token_issuer(req),
297 "ListTrustedTokenIssuers" => s.list_trusted_token_issuers(req),
298 "AddRegion" => s.add_region(req),
300 "RemoveRegion" => s.remove_region(req),
301 "DescribeRegion" => s.describe_region(req),
302 "ListRegions" => s.list_regions(req),
303 "CreateInstanceAccessControlAttributeConfiguration" => s.create_attr_config(req),
305 "DescribeInstanceAccessControlAttributeConfiguration" => s.describe_attr_config(req),
306 "UpdateInstanceAccessControlAttributeConfiguration" => s.update_attr_config(req),
307 "DeleteInstanceAccessControlAttributeConfiguration" => s.delete_attr_config(req),
308 "TagResource" => s.tag_resource(req),
310 "UntagResource" => s.untag_resource(req),
311 "ListTagsForResource" => s.list_tags_for_resource(req),
312 _ => Err(AwsServiceError::action_not_implemented(
313 s.service_name(),
314 &req.action,
315 )),
316 }
317}
318
319fn ok(v: Value) -> Result<AwsResponse, AwsServiceError> {
322 Ok(AwsResponse::json_value(StatusCode::OK, v))
323}
324
325fn parse(req: &AwsRequest) -> Result<Value, AwsServiceError> {
326 if req.body.is_empty() {
327 return Ok(json!({}));
328 }
329 serde_json::from_slice(&req.body)
330 .map_err(|e| validation(&format!("Request body is malformed: {e}")))
331}
332
333fn validation(msg: &str) -> AwsServiceError {
334 AwsServiceError::aws_error(StatusCode::BAD_REQUEST, "ValidationException", msg)
335}
336
337fn not_found(msg: &str) -> AwsServiceError {
338 AwsServiceError::aws_error(StatusCode::NOT_FOUND, "ResourceNotFoundException", msg)
339}
340
341fn req_str<'a>(b: &'a Value, f: &str) -> Result<&'a str, AwsServiceError> {
342 b.get(f)
343 .and_then(Value::as_str)
344 .filter(|s| !s.is_empty())
345 .ok_or_else(|| validation(&format!("{f} must be specified.")))
346}
347
348fn check_len(b: &Value, field: &str, min: usize, max: usize) -> Result<(), AwsServiceError> {
351 if let Some(s) = b.get(field).and_then(Value::as_str) {
352 let n = s.chars().count();
353 if n < min || n > max {
354 return Err(validation(&format!(
355 "{field} must have length between {min} and {max}, inclusive."
356 )));
357 }
358 }
359 Ok(())
360}
361
362fn check_enum(b: &Value, field: &str, allowed: &[&str]) -> Result<(), AwsServiceError> {
364 if let Some(s) = b.get(field).and_then(Value::as_str) {
365 if !allowed.contains(&s) {
366 return Err(validation(&format!(
367 "{field} must be one of [{}].",
368 allowed.join(", ")
369 )));
370 }
371 }
372 Ok(())
373}
374
375fn check_range(b: &Value, field: &str, min: i64, max: i64) -> Result<(), AwsServiceError> {
377 if let Some(v) = b.get(field) {
378 let n = v.as_i64().unwrap_or(min - 1);
379 if n < min || n > max {
380 return Err(validation(&format!(
381 "{field} must be between {min} and {max}, inclusive."
382 )));
383 }
384 }
385 Ok(())
386}
387
388fn validate_common(b: &Value) -> Result<(), AwsServiceError> {
392 check_len(b, "InstanceArn", 10, 1224)?;
393 check_len(b, "PermissionSetArn", 10, 1224)?;
394 check_len(b, "ApplicationArn", 10, 1224)?;
395 check_len(b, "ApplicationProviderArn", 10, 1224)?;
396 check_len(b, "TrustedTokenIssuerArn", 10, 1224)?;
397 check_len(b, "ResourceArn", 10, 2048)?;
398 check_len(b, "ManagedPolicyArn", 20, 2048)?;
399 check_len(b, "PrincipalId", 1, 47)?;
400 check_len(b, "TargetId", 12, 12)?;
401 check_len(b, "AccountId", 12, 12)?;
402 check_len(b, "RegionName", 1, 32)?;
403 check_len(b, "NextToken", 0, 2048)?;
404 check_len(b, "ClientToken", 1, 64)?;
405 check_len(b, "RelayState", 1, 240)?;
406 check_len(b, "SessionDuration", 1, 100)?;
407 check_len(b, "InlinePolicy", 1, 32768)?;
408 check_len(b, "AccountAssignmentCreationRequestId", 36, 36)?;
409 check_len(b, "AccountAssignmentDeletionRequestId", 36, 36)?;
410 check_len(b, "ProvisionPermissionSetRequestId", 36, 36)?;
411 check_range(b, "MaxResults", 1, 100)?;
412 check_enum(b, "PrincipalType", &["USER", "GROUP"])?;
413 check_enum(
414 b,
415 "TargetType",
416 &["AWS_ACCOUNT", "ALL_PROVISIONED_ACCOUNTS"],
417 )?;
418 check_enum(
419 b,
420 "GrantType",
421 &[
422 "AUTHORIZATION_CODE",
423 "REFRESH_TOKEN",
424 "JWT_BEARER",
425 "TOKEN_EXCHANGE",
426 ],
427 )?;
428 check_enum(b, "AuthenticationMethodType", &["IAM"])?;
429 check_enum(
430 b,
431 "ProvisioningStatus",
432 &[
433 "LATEST_PERMISSION_SET_PROVISIONED",
434 "LATEST_PERMISSION_SET_NOT_PROVISIONED",
435 ],
436 )?;
437 check_enum(b, "Status", &["ENABLED", "DISABLED"])?;
438 check_enum(b, "TrustedTokenIssuerType", &["OIDC_JWT"])?;
439 check_enum(
440 b,
441 "UserBackgroundSessionApplicationStatus",
442 &["ENABLED", "DISABLED"],
443 )?;
444 Ok(())
445}
446
447fn now() -> i64 {
448 chrono::Utc::now().timestamp()
449}
450
451fn hex16() -> String {
452 Uuid::new_v4().simple().to_string()[..16].to_string()
453}
454
455fn hex10() -> String {
456 Uuid::new_v4().simple().to_string()[..10].to_string()
457}
458
459fn paginate(rows: Vec<Value>, b: &Value) -> (Vec<Value>, Option<String>) {
461 let start = b
462 .get("NextToken")
463 .and_then(Value::as_str)
464 .and_then(|t| t.parse::<usize>().ok())
465 .unwrap_or(0);
466 let max = b
467 .get("MaxResults")
468 .and_then(Value::as_u64)
469 .map(|m| m.clamp(1, 100) as usize)
470 .unwrap_or(100);
471 let end = (start + max).min(rows.len());
472 let page = rows.get(start..end).unwrap_or(&[]).to_vec();
473 let next = if end < rows.len() {
474 Some(end.to_string())
475 } else {
476 None
477 };
478 (page, next)
479}
480
481fn list_response(key: &str, rows: Vec<Value>, b: &Value) -> Result<AwsResponse, AwsServiceError> {
483 let (page, next) = paginate(rows, b);
484 let mut out = json!({ key: page });
485 if let Some(t) = next {
486 out["NextToken"] = json!(t);
487 }
488 ok(out)
489}
490
491fn opt_str(m: &mut serde_json::Map<String, Value>, key: &str, v: &Option<String>) {
492 if let Some(val) = v {
493 m.insert(key.to_string(), json!(val));
494 }
495}
496
497fn build_permission_set(ps: &StoredPermissionSet) -> Value {
498 let mut m = serde_json::Map::new();
499 m.insert("PermissionSetArn".into(), json!(ps.arn));
500 m.insert("Name".into(), json!(ps.name));
501 m.insert("CreatedDate".into(), json!(ps.created_date));
502 opt_str(&mut m, "Description", &ps.description);
503 opt_str(&mut m, "SessionDuration", &ps.session_duration);
504 opt_str(&mut m, "RelayState", &ps.relay_state);
505 Value::Object(m)
506}
507
508fn build_region(r: &StoredRegion) -> Value {
509 json!({
510 "RegionName": r.region_name,
511 "Status": r.status,
512 "AddedDate": r.added_date,
513 "IsPrimaryRegion": r.is_primary,
514 })
515}
516
517fn build_instance_describe(inst: &StoredInstance) -> Value {
518 let mut m = serde_json::Map::new();
519 m.insert("InstanceArn".into(), json!(inst.arn));
520 m.insert("IdentityStoreId".into(), json!(inst.identity_store_id));
521 m.insert("OwnerAccountId".into(), json!(inst.owner_account_id));
522 m.insert("CreatedDate".into(), json!(inst.created_date));
523 m.insert("Status".into(), json!(inst.status));
524 opt_str(&mut m, "Name", &inst.name);
525 Value::Object(m)
526}
527
528fn build_instance_metadata(inst: &StoredInstance) -> Value {
529 let mut m = serde_json::Map::new();
530 m.insert("InstanceArn".into(), json!(inst.arn));
531 m.insert("IdentityStoreId".into(), json!(inst.identity_store_id));
532 m.insert("OwnerAccountId".into(), json!(inst.owner_account_id));
533 m.insert("CreatedDate".into(), json!(inst.created_date));
534 m.insert("Status".into(), json!(inst.status));
535 opt_str(&mut m, "Name", &inst.name);
536 opt_str(&mut m, "PrimaryRegion", &inst.primary_region);
537 let regions: Vec<Value> = inst.regions.iter().map(build_region).collect();
538 m.insert("Regions".into(), json!(regions));
539 Value::Object(m)
540}
541
542fn build_application(app: &StoredApplication) -> Value {
543 let mut m = serde_json::Map::new();
544 m.insert("ApplicationArn".into(), json!(app.arn));
545 m.insert("ApplicationProviderArn".into(), json!(app.provider_arn));
546 m.insert("ApplicationAccount".into(), json!(app.account));
547 m.insert("InstanceArn".into(), json!(app.instance_arn));
548 m.insert("IdentityStoreArn".into(), json!(app.identity_store_arn));
549 m.insert("CreatedDate".into(), json!(app.created_date));
550 opt_str(&mut m, "Name", &app.name);
551 opt_str(&mut m, "Status", &app.status);
552 opt_str(&mut m, "Description", &app.description);
553 opt_str(&mut m, "CreatedFrom", &app.created_from);
554 if let Some(po) = &app.portal_options {
555 m.insert("PortalOptions".into(), po.clone());
556 }
557 Value::Object(m)
558}
559
560fn build_tti_describe(tti: &StoredTrustedTokenIssuer) -> Value {
561 let mut m = serde_json::Map::new();
562 m.insert("TrustedTokenIssuerArn".into(), json!(tti.arn));
563 m.insert("TrustedTokenIssuerType".into(), json!(tti.issuer_type));
564 opt_str(&mut m, "Name", &tti.name);
565 if let Some(cfg) = &tti.config {
566 m.insert("TrustedTokenIssuerConfiguration".into(), cfg.clone());
567 }
568 Value::Object(m)
569}
570
571fn build_tti_metadata(tti: &StoredTrustedTokenIssuer) -> Value {
572 let mut m = serde_json::Map::new();
573 m.insert("TrustedTokenIssuerArn".into(), json!(tti.arn));
574 m.insert("TrustedTokenIssuerType".into(), json!(tti.issuer_type));
575 opt_str(&mut m, "Name", &tti.name);
576 Value::Object(m)
577}
578
579fn build_op_status(st: &StoredOpStatus) -> Value {
580 json!({
581 "Status": st.status,
582 "RequestId": st.request_id,
583 "TargetId": st.target_id,
584 "TargetType": st.target_type,
585 "PermissionSetArn": st.permission_set_arn,
586 "PrincipalType": st.principal_type,
587 "PrincipalId": st.principal_id,
588 "CreatedDate": st.created_date,
589 })
590}
591
592fn build_prov_status(st: &StoredProvisioningStatus) -> Value {
593 let mut m = serde_json::Map::new();
594 m.insert("Status".into(), json!(st.status));
595 m.insert("RequestId".into(), json!(st.request_id));
596 m.insert("PermissionSetArn".into(), json!(st.permission_set_arn));
597 m.insert("CreatedDate".into(), json!(st.created_date));
598 if let Some(acct) = &st.account_id {
599 m.insert("AccountId".into(), json!(acct));
600 }
601 Value::Object(m)
602}
603
604fn store_tags(acct: &mut crate::state::SsoAdminData, arn: &str, b: &Value) {
607 let Some(tags) = b.get("Tags").and_then(Value::as_array) else {
608 return;
609 };
610 if tags.is_empty() {
611 return;
612 }
613 let entry = acct.tags.entry(arn.to_string()).or_default();
614 for tag in tags {
615 if let (Some(k), Some(v)) = (
616 tag.get("Key").and_then(Value::as_str),
617 tag.get("Value").and_then(Value::as_str),
618 ) {
619 entry.insert(k.to_string(), v.to_string());
620 }
621 }
622}
623
624fn portal_options_with_defaults(po: Option<Value>) -> Option<Value> {
628 po.map(|mut v| {
629 if let Value::Object(m) = &mut v {
630 m.entry("Visibility").or_insert(json!("ENABLED"));
631 }
632 v
633 })
634}
635
636fn deep_merge(base: &mut Value, patch: &Value) {
641 match (base, patch) {
642 (Value::Object(b), Value::Object(p)) => {
643 for (k, v) in p {
644 deep_merge(b.entry(k.clone()).or_insert(Value::Null), v);
645 }
646 }
647 (b, p) => *b = p.clone(),
648 }
649}
650
651impl SsoAdminService {
652 fn create_instance(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
655 let b = parse(req)?;
656 validate_common(&b)?;
657 check_len(&b, "Name", 0, 255)?;
658 let ssoins = format!("ssoins-{}", hex16());
659 let arn = format!("arn:aws:sso:::instance/{ssoins}");
660 let identity_store_id = format!("d-{}", hex10());
661 let inst = StoredInstance {
662 arn: arn.clone(),
663 ssoins,
664 identity_store_id,
665 owner_account_id: req.account_id.clone(),
666 name: b.get("Name").and_then(Value::as_str).map(str::to_string),
667 status: "ACTIVE".into(),
668 created_date: now(),
669 regions: Vec::new(),
670 primary_region: Some(req.region.clone()),
671 attr_config: None,
672 attr_config_status: None,
673 };
674 {
675 let mut guard = self.state.write();
676 let acct = guard.get_or_create(&req.account_id);
677 acct.instances.insert(arn.clone(), inst);
678 store_tags(acct, &arn, &b);
679 }
680 ok(json!({ "InstanceArn": arn }))
681 }
682
683 fn describe_instance(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
684 let b = parse(req)?;
685 validate_common(&b)?;
686 let arn = req_str(&b, "InstanceArn")?.to_string();
687 let guard = self.state.read();
688 if let Some(inst) = guard
689 .get(&req.account_id)
690 .and_then(|a| a.instances.get(&arn))
691 {
692 return ok(build_instance_describe(inst));
693 }
694 let ssoins = arn.rsplit('/').next().unwrap_or("ssoins-unknown");
698 let synth = StoredInstance {
699 arn: arn.clone(),
700 ssoins: ssoins.to_string(),
701 identity_store_id: format!("d-{}", hex10()),
702 owner_account_id: req.account_id.clone(),
703 name: None,
704 status: "ACTIVE".into(),
705 created_date: now(),
706 regions: Vec::new(),
707 primary_region: Some(req.region.clone()),
708 attr_config: None,
709 attr_config_status: None,
710 };
711 ok(build_instance_describe(&synth))
712 }
713
714 fn update_instance(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
715 let b = parse(req)?;
716 validate_common(&b)?;
717 check_len(&b, "Name", 0, 255)?;
718 let arn = req_str(&b, "InstanceArn")?.to_string();
719 let mut guard = self.state.write();
720 let inst = guard
721 .get_mut(&req.account_id)
722 .and_then(|a| a.instances.get_mut(&arn))
723 .ok_or_else(|| not_found("Instance not found."))?;
724 if let Some(name) = b.get("Name").and_then(Value::as_str) {
725 inst.name = Some(name.to_string());
726 }
727 ok(json!({}))
728 }
729
730 fn delete_instance(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
731 let b = parse(req)?;
732 validate_common(&b)?;
733 let arn = req_str(&b, "InstanceArn")?.to_string();
734 if let Some(a) = self.state.write().get_mut(&req.account_id) {
735 a.instances.remove(&arn);
736 }
737 ok(json!({}))
738 }
739
740 fn list_instances(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
741 let b = parse(req)?;
742 validate_common(&b)?;
743 let guard = self.state.read();
744 let rows: Vec<Value> = guard
745 .get(&req.account_id)
746 .map(|a| a.instances.values().map(build_instance_metadata).collect())
747 .unwrap_or_default();
748 list_response("Instances", rows, &b)
749 }
750
751 fn permission_set<'a>(
754 &self,
755 guard: &'a fakecloud_core::multi_account::MultiAccountState<crate::state::SsoAdminData>,
756 account: &str,
757 arn: &str,
758 ) -> Option<&'a StoredPermissionSet> {
759 guard.get(account).and_then(|a| a.permission_sets.get(arn))
760 }
761
762 fn create_permission_set(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
763 let b = parse(req)?;
764 validate_common(&b)?;
765 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
766 let name = req_str(&b, "Name")?.to_string();
767 check_len(&b, "Name", 1, 32)?;
768 check_len(&b, "Description", 1, 700)?;
769 let mut guard = self.state.write();
770 let acct = guard.get_or_create(&req.account_id);
771 let inst = acct
772 .instances
773 .get(&instance_arn)
774 .ok_or_else(|| not_found("Instance not found."))?;
775 let ssoins = inst.ssoins.clone();
776 let arn = format!("arn:aws:sso:::permissionSet/{ssoins}/ps-{}", hex16());
777 let ps = StoredPermissionSet {
778 arn: arn.clone(),
779 instance_arn,
780 name,
781 description: b
782 .get("Description")
783 .and_then(Value::as_str)
784 .map(str::to_string),
785 session_duration: b
786 .get("SessionDuration")
787 .and_then(Value::as_str)
788 .map(str::to_string),
789 relay_state: b
790 .get("RelayState")
791 .and_then(Value::as_str)
792 .map(str::to_string),
793 created_date: now(),
794 inline_policy: None,
795 permissions_boundary: None,
796 managed_policies: Vec::new(),
797 customer_managed: Vec::new(),
798 };
799 acct.permission_sets.insert(arn.clone(), ps.clone());
800 store_tags(acct, &arn, &b);
801 ok(json!({ "PermissionSet": build_permission_set(&ps) }))
802 }
803
804 fn describe_permission_set(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
805 let b = parse(req)?;
806 validate_common(&b)?;
807 req_str(&b, "InstanceArn")?;
808 let arn = req_str(&b, "PermissionSetArn")?.to_string();
809 let guard = self.state.read();
810 let ps = self
811 .permission_set(&guard, &req.account_id, &arn)
812 .ok_or_else(|| not_found("Permission set not found."))?;
813 ok(json!({ "PermissionSet": build_permission_set(ps) }))
814 }
815
816 fn update_permission_set(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
817 let b = parse(req)?;
818 validate_common(&b)?;
819 check_len(&b, "Description", 1, 700)?;
820 req_str(&b, "InstanceArn")?;
821 let arn = req_str(&b, "PermissionSetArn")?.to_string();
822 let mut guard = self.state.write();
823 let ps = guard
824 .get_mut(&req.account_id)
825 .and_then(|a| a.permission_sets.get_mut(&arn))
826 .ok_or_else(|| not_found("Permission set not found."))?;
827 if let Some(d) = b.get("Description").and_then(Value::as_str) {
828 ps.description = Some(d.to_string());
829 }
830 if let Some(d) = b.get("SessionDuration").and_then(Value::as_str) {
831 ps.session_duration = Some(d.to_string());
832 }
833 if let Some(d) = b.get("RelayState").and_then(Value::as_str) {
834 ps.relay_state = Some(d.to_string());
835 }
836 ok(json!({}))
837 }
838
839 fn delete_permission_set(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
840 let b = parse(req)?;
841 validate_common(&b)?;
842 req_str(&b, "InstanceArn")?;
843 let arn = req_str(&b, "PermissionSetArn")?.to_string();
844 if let Some(a) = self.state.write().get_mut(&req.account_id) {
845 a.permission_sets.remove(&arn);
846 }
847 ok(json!({}))
848 }
849
850 fn list_permission_sets(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
851 let b = parse(req)?;
852 validate_common(&b)?;
853 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
854 let guard = self.state.read();
855 let rows: Vec<Value> = guard
856 .get(&req.account_id)
857 .map(|a| {
858 a.permission_sets
859 .values()
860 .filter(|ps| ps.instance_arn == instance_arn)
861 .map(|ps| json!(ps.arn))
862 .collect()
863 })
864 .unwrap_or_default();
865 list_response("PermissionSets", rows, &b)
866 }
867
868 fn with_permission_set<F>(
871 &self,
872 req: &AwsRequest,
873 b: &Value,
874 f: F,
875 ) -> Result<AwsResponse, AwsServiceError>
876 where
877 F: FnOnce(&mut StoredPermissionSet) -> Result<AwsResponse, AwsServiceError>,
878 {
879 let arn = req_str(b, "PermissionSetArn")?.to_string();
880 let mut guard = self.state.write();
881 let ps = guard
882 .get_mut(&req.account_id)
883 .and_then(|a| a.permission_sets.get_mut(&arn))
884 .ok_or_else(|| not_found("Permission set not found."))?;
885 f(ps)
886 }
887
888 fn attach_managed_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
889 let b = parse(req)?;
890 validate_common(&b)?;
891 req_str(&b, "InstanceArn")?;
892 let policy_arn = req_str(&b, "ManagedPolicyArn")?.to_string();
893 let name = policy_arn
894 .rsplit('/')
895 .next()
896 .unwrap_or(&policy_arn)
897 .to_string();
898 self.with_permission_set(req, &b, |ps| {
899 if !ps.managed_policies.iter().any(|p| p.arn == policy_arn) {
900 ps.managed_policies.push(StoredManagedPolicy {
901 name,
902 arn: policy_arn,
903 });
904 }
905 ok(json!({}))
906 })
907 }
908
909 fn detach_managed_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
910 let b = parse(req)?;
911 validate_common(&b)?;
912 req_str(&b, "InstanceArn")?;
913 let policy_arn = req_str(&b, "ManagedPolicyArn")?.to_string();
914 self.with_permission_set(req, &b, |ps| {
915 ps.managed_policies.retain(|p| p.arn != policy_arn);
916 ok(json!({}))
917 })
918 }
919
920 fn list_managed_policies(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
921 let b = parse(req)?;
922 validate_common(&b)?;
923 req_str(&b, "InstanceArn")?;
924 let arn = req_str(&b, "PermissionSetArn")?.to_string();
925 let guard = self.state.read();
926 let rows: Vec<Value> = self
927 .permission_set(&guard, &req.account_id, &arn)
928 .map(|ps| {
929 ps.managed_policies
930 .iter()
931 .map(|p| json!({ "Name": p.name, "Arn": p.arn }))
932 .collect()
933 })
934 .unwrap_or_default();
935 list_response("AttachedManagedPolicies", rows, &b)
936 }
937
938 fn attach_customer_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
939 let b = parse(req)?;
940 validate_common(&b)?;
941 req_str(&b, "InstanceArn")?;
942 let reference = b
943 .get("CustomerManagedPolicyReference")
944 .cloned()
945 .ok_or_else(|| validation("CustomerManagedPolicyReference must be specified."))?;
946 let ref_name = reference
947 .get("Name")
948 .and_then(Value::as_str)
949 .map(str::to_string);
950 self.with_permission_set(req, &b, |ps| {
951 let exists = ps
952 .customer_managed
953 .iter()
954 .any(|r| r.get("Name").and_then(Value::as_str).map(str::to_string) == ref_name);
955 if !exists {
956 ps.customer_managed.push(reference);
957 }
958 ok(json!({}))
959 })
960 }
961
962 fn detach_customer_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
963 let b = parse(req)?;
964 validate_common(&b)?;
965 req_str(&b, "InstanceArn")?;
966 let reference = b
967 .get("CustomerManagedPolicyReference")
968 .cloned()
969 .ok_or_else(|| validation("CustomerManagedPolicyReference must be specified."))?;
970 let ref_name = reference
971 .get("Name")
972 .and_then(Value::as_str)
973 .map(str::to_string);
974 self.with_permission_set(req, &b, |ps| {
975 ps.customer_managed
976 .retain(|r| r.get("Name").and_then(Value::as_str).map(str::to_string) != ref_name);
977 ok(json!({}))
978 })
979 }
980
981 fn list_customer_policies(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
982 let b = parse(req)?;
983 validate_common(&b)?;
984 req_str(&b, "InstanceArn")?;
985 let arn = req_str(&b, "PermissionSetArn")?.to_string();
986 let guard = self.state.read();
987 let rows: Vec<Value> = self
988 .permission_set(&guard, &req.account_id, &arn)
989 .map(|ps| ps.customer_managed.clone())
990 .unwrap_or_default();
991 list_response("CustomerManagedPolicyReferences", rows, &b)
992 }
993
994 fn put_inline_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
995 let b = parse(req)?;
996 validate_common(&b)?;
997 req_str(&b, "InstanceArn")?;
998 let policy = req_str(&b, "InlinePolicy")?.to_string();
999 self.with_permission_set(req, &b, |ps| {
1000 ps.inline_policy = Some(policy);
1001 ok(json!({}))
1002 })
1003 }
1004
1005 fn get_inline_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1006 let b = parse(req)?;
1007 validate_common(&b)?;
1008 req_str(&b, "InstanceArn")?;
1009 let arn = req_str(&b, "PermissionSetArn")?.to_string();
1010 let guard = self.state.read();
1011 let ps = self
1012 .permission_set(&guard, &req.account_id, &arn)
1013 .ok_or_else(|| not_found("Permission set not found."))?;
1014 ok(json!({ "InlinePolicy": ps.inline_policy.clone().unwrap_or_default() }))
1016 }
1017
1018 fn delete_inline_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1019 let b = parse(req)?;
1020 validate_common(&b)?;
1021 req_str(&b, "InstanceArn")?;
1022 self.with_permission_set(req, &b, |ps| {
1023 ps.inline_policy = None;
1024 ok(json!({}))
1025 })
1026 }
1027
1028 fn put_permissions_boundary(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1029 let b = parse(req)?;
1030 validate_common(&b)?;
1031 req_str(&b, "InstanceArn")?;
1032 let boundary = b
1033 .get("PermissionsBoundary")
1034 .cloned()
1035 .ok_or_else(|| validation("PermissionsBoundary must be specified."))?;
1036 if boundary.get("ManagedPolicyArn").is_some()
1039 && boundary.get("CustomerManagedPolicyReference").is_some()
1040 {
1041 return Err(validation(
1042 "Only ManagedPolicyArn or CustomerManagedPolicyReference should be given.",
1043 ));
1044 }
1045 self.with_permission_set(req, &b, |ps| {
1046 ps.permissions_boundary = Some(boundary);
1047 ok(json!({}))
1048 })
1049 }
1050
1051 fn get_permissions_boundary(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1052 let b = parse(req)?;
1053 validate_common(&b)?;
1054 req_str(&b, "InstanceArn")?;
1055 let arn = req_str(&b, "PermissionSetArn")?.to_string();
1056 let guard = self.state.read();
1057 let ps = self
1058 .permission_set(&guard, &req.account_id, &arn)
1059 .ok_or_else(|| not_found("Permission set not found."))?;
1060 let boundary = ps
1061 .permissions_boundary
1062 .clone()
1063 .ok_or_else(|| not_found("No permissions boundary is set."))?;
1064 ok(json!({ "PermissionsBoundary": boundary }))
1065 }
1066
1067 fn delete_permissions_boundary(
1068 &self,
1069 req: &AwsRequest,
1070 ) -> Result<AwsResponse, AwsServiceError> {
1071 let b = parse(req)?;
1072 validate_common(&b)?;
1073 req_str(&b, "InstanceArn")?;
1074 self.with_permission_set(req, &b, |ps| {
1075 ps.permissions_boundary = None;
1076 ok(json!({}))
1077 })
1078 }
1079
1080 fn create_account_assignment(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1083 let b = parse(req)?;
1084 validate_common(&b)?;
1085 req_str(&b, "InstanceArn")?;
1086 let target_id = req_str(&b, "TargetId")?.to_string();
1087 req_str(&b, "TargetType")?;
1088 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1089 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1090 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1091 let request_id = Uuid::new_v4().to_string();
1092 let st = StoredOpStatus {
1093 request_id: request_id.clone(),
1094 status: "IN_PROGRESS".into(),
1095 target_id: target_id.clone(),
1096 target_type: b
1097 .get("TargetType")
1098 .and_then(Value::as_str)
1099 .unwrap_or("AWS_ACCOUNT")
1100 .to_string(),
1101 permission_set_arn: ps_arn.clone(),
1102 principal_type: principal_type.clone(),
1103 principal_id: principal_id.clone(),
1104 created_date: now(),
1105 };
1106 let mut guard = self.state.write();
1107 let acct = guard.get_or_create(&req.account_id);
1108 if !acct.assignments.iter().any(|a| {
1110 a.account_id == target_id
1111 && a.permission_set_arn == ps_arn
1112 && a.principal_id == principal_id
1113 }) {
1114 acct.assignments.push(StoredAssignment {
1115 account_id: target_id,
1116 permission_set_arn: ps_arn,
1117 principal_type,
1118 principal_id,
1119 });
1120 }
1121 acct.aa_create_status.insert(request_id, st.clone());
1122 ok(json!({ "AccountAssignmentCreationStatus": build_op_status(&st) }))
1123 }
1124
1125 fn delete_account_assignment(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1126 let b = parse(req)?;
1127 validate_common(&b)?;
1128 req_str(&b, "InstanceArn")?;
1129 let target_id = req_str(&b, "TargetId")?.to_string();
1130 req_str(&b, "TargetType")?;
1131 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1132 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1133 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1134 let request_id = Uuid::new_v4().to_string();
1135 let st = StoredOpStatus {
1136 request_id: request_id.clone(),
1137 status: "IN_PROGRESS".into(),
1138 target_id: target_id.clone(),
1139 target_type: b
1140 .get("TargetType")
1141 .and_then(Value::as_str)
1142 .unwrap_or("AWS_ACCOUNT")
1143 .to_string(),
1144 permission_set_arn: ps_arn.clone(),
1145 principal_type,
1146 principal_id: principal_id.clone(),
1147 created_date: now(),
1148 };
1149 let mut guard = self.state.write();
1150 let acct = guard.get_or_create(&req.account_id);
1151 acct.assignments.retain(|a| {
1152 !(a.account_id == target_id
1153 && a.permission_set_arn == ps_arn
1154 && a.principal_id == principal_id)
1155 });
1156 acct.aa_delete_status.insert(request_id, st.clone());
1157 ok(json!({ "AccountAssignmentDeletionStatus": build_op_status(&st) }))
1158 }
1159
1160 fn describe_aa_creation_status(
1161 &self,
1162 req: &AwsRequest,
1163 ) -> Result<AwsResponse, AwsServiceError> {
1164 let b = parse(req)?;
1165 validate_common(&b)?;
1166 req_str(&b, "InstanceArn")?;
1167 let id = req_str(&b, "AccountAssignmentCreationRequestId")?.to_string();
1168 let mut guard = self.state.write();
1169 let st = guard
1170 .get_mut(&req.account_id)
1171 .and_then(|a| a.aa_create_status.get_mut(&id))
1172 .ok_or_else(|| not_found("Assignment creation status not found."))?;
1173 st.status = "SUCCEEDED".into();
1174 ok(json!({ "AccountAssignmentCreationStatus": build_op_status(st) }))
1175 }
1176
1177 fn describe_aa_deletion_status(
1178 &self,
1179 req: &AwsRequest,
1180 ) -> Result<AwsResponse, AwsServiceError> {
1181 let b = parse(req)?;
1182 validate_common(&b)?;
1183 req_str(&b, "InstanceArn")?;
1184 let id = req_str(&b, "AccountAssignmentDeletionRequestId")?.to_string();
1185 let mut guard = self.state.write();
1186 let st = guard
1187 .get_mut(&req.account_id)
1188 .and_then(|a| a.aa_delete_status.get_mut(&id))
1189 .ok_or_else(|| not_found("Assignment deletion status not found."))?;
1190 st.status = "SUCCEEDED".into();
1191 ok(json!({ "AccountAssignmentDeletionStatus": build_op_status(st) }))
1192 }
1193
1194 fn list_aa_creation_status(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1195 let b = parse(req)?;
1196 validate_common(&b)?;
1197 req_str(&b, "InstanceArn")?;
1198 let guard = self.state.read();
1199 let rows: Vec<Value> = guard
1200 .get(&req.account_id)
1201 .map(|a| {
1202 a.aa_create_status
1203 .values()
1204 .map(|st| json!({ "Status": st.status, "RequestId": st.request_id, "CreatedDate": st.created_date }))
1205 .collect()
1206 })
1207 .unwrap_or_default();
1208 list_response("AccountAssignmentsCreationStatus", rows, &b)
1209 }
1210
1211 fn list_aa_deletion_status(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1212 let b = parse(req)?;
1213 validate_common(&b)?;
1214 req_str(&b, "InstanceArn")?;
1215 let guard = self.state.read();
1216 let rows: Vec<Value> = guard
1217 .get(&req.account_id)
1218 .map(|a| {
1219 a.aa_delete_status
1220 .values()
1221 .map(|st| json!({ "Status": st.status, "RequestId": st.request_id, "CreatedDate": st.created_date }))
1222 .collect()
1223 })
1224 .unwrap_or_default();
1225 list_response("AccountAssignmentsDeletionStatus", rows, &b)
1226 }
1227
1228 fn list_account_assignments(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1229 let b = parse(req)?;
1230 validate_common(&b)?;
1231 req_str(&b, "InstanceArn")?;
1232 let account_id = req_str(&b, "AccountId")?.to_string();
1233 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1234 let guard = self.state.read();
1235 let rows: Vec<Value> = guard
1236 .get(&req.account_id)
1237 .map(|a| {
1238 a.assignments
1239 .iter()
1240 .filter(|x| x.account_id == account_id && x.permission_set_arn == ps_arn)
1241 .map(|x| {
1242 json!({
1243 "AccountId": x.account_id,
1244 "PermissionSetArn": x.permission_set_arn,
1245 "PrincipalType": x.principal_type,
1246 "PrincipalId": x.principal_id,
1247 })
1248 })
1249 .collect()
1250 })
1251 .unwrap_or_default();
1252 list_response("AccountAssignments", rows, &b)
1253 }
1254
1255 fn list_account_assignments_for_principal(
1256 &self,
1257 req: &AwsRequest,
1258 ) -> Result<AwsResponse, AwsServiceError> {
1259 let b = parse(req)?;
1260 validate_common(&b)?;
1261 req_str(&b, "InstanceArn")?;
1262 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1263 req_str(&b, "PrincipalType")?;
1264 let guard = self.state.read();
1265 let rows: Vec<Value> = guard
1266 .get(&req.account_id)
1267 .map(|a| {
1268 a.assignments
1269 .iter()
1270 .filter(|x| x.principal_id == principal_id)
1271 .map(|x| {
1272 json!({
1273 "AccountId": x.account_id,
1274 "PermissionSetArn": x.permission_set_arn,
1275 "PrincipalId": x.principal_id,
1276 "PrincipalType": x.principal_type,
1277 })
1278 })
1279 .collect()
1280 })
1281 .unwrap_or_default();
1282 list_response("AccountAssignments", rows, &b)
1283 }
1284
1285 fn list_accounts_for_provisioned(
1286 &self,
1287 req: &AwsRequest,
1288 ) -> Result<AwsResponse, AwsServiceError> {
1289 let b = parse(req)?;
1290 validate_common(&b)?;
1291 req_str(&b, "InstanceArn")?;
1292 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1293 let guard = self.state.read();
1294 let mut accounts: Vec<String> = guard
1295 .get(&req.account_id)
1296 .map(|a| {
1297 a.assignments
1298 .iter()
1299 .filter(|x| x.permission_set_arn == ps_arn)
1300 .map(|x| x.account_id.clone())
1301 .collect()
1302 })
1303 .unwrap_or_default();
1304 accounts.sort();
1305 accounts.dedup();
1306 let rows: Vec<Value> = accounts.into_iter().map(|a| json!(a)).collect();
1307 list_response("AccountIds", rows, &b)
1308 }
1309
1310 fn list_ps_provisioned_to_account(
1311 &self,
1312 req: &AwsRequest,
1313 ) -> Result<AwsResponse, AwsServiceError> {
1314 let b = parse(req)?;
1315 validate_common(&b)?;
1316 req_str(&b, "InstanceArn")?;
1317 let account_id = req_str(&b, "AccountId")?.to_string();
1318 let guard = self.state.read();
1319 let mut ps: Vec<String> = guard
1320 .get(&req.account_id)
1321 .map(|a| {
1322 a.assignments
1323 .iter()
1324 .filter(|x| x.account_id == account_id)
1325 .map(|x| x.permission_set_arn.clone())
1326 .collect()
1327 })
1328 .unwrap_or_default();
1329 ps.sort();
1330 ps.dedup();
1331 let rows: Vec<Value> = ps.into_iter().map(|a| json!(a)).collect();
1332 list_response("PermissionSets", rows, &b)
1333 }
1334
1335 fn provision_permission_set(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1338 let b = parse(req)?;
1339 validate_common(&b)?;
1340 req_str(&b, "InstanceArn")?;
1341 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1342 req_str(&b, "TargetType")?;
1343 let request_id = Uuid::new_v4().to_string();
1344 let st = StoredProvisioningStatus {
1345 request_id: request_id.clone(),
1346 status: "IN_PROGRESS".into(),
1347 account_id: b
1348 .get("TargetId")
1349 .and_then(Value::as_str)
1350 .map(str::to_string),
1351 permission_set_arn: ps_arn,
1352 created_date: now(),
1353 };
1354 self.state
1355 .write()
1356 .get_or_create(&req.account_id)
1357 .ps_provisioning
1358 .insert(request_id, st.clone());
1359 ok(json!({ "PermissionSetProvisioningStatus": build_prov_status(&st) }))
1360 }
1361
1362 fn describe_ps_provisioning_status(
1363 &self,
1364 req: &AwsRequest,
1365 ) -> Result<AwsResponse, AwsServiceError> {
1366 let b = parse(req)?;
1367 validate_common(&b)?;
1368 req_str(&b, "InstanceArn")?;
1369 let id = req_str(&b, "ProvisionPermissionSetRequestId")?.to_string();
1370 let mut guard = self.state.write();
1371 let st = guard
1372 .get_mut(&req.account_id)
1373 .and_then(|a| a.ps_provisioning.get_mut(&id))
1374 .ok_or_else(|| not_found("Provisioning status not found."))?;
1375 st.status = "SUCCEEDED".into();
1376 ok(json!({ "PermissionSetProvisioningStatus": build_prov_status(st) }))
1377 }
1378
1379 fn list_ps_provisioning_status(
1380 &self,
1381 req: &AwsRequest,
1382 ) -> Result<AwsResponse, AwsServiceError> {
1383 let b = parse(req)?;
1384 validate_common(&b)?;
1385 req_str(&b, "InstanceArn")?;
1386 let guard = self.state.read();
1387 let rows: Vec<Value> = guard
1388 .get(&req.account_id)
1389 .map(|a| {
1390 a.ps_provisioning
1391 .values()
1392 .map(|st| json!({ "Status": st.status, "RequestId": st.request_id, "CreatedDate": st.created_date }))
1393 .collect()
1394 })
1395 .unwrap_or_default();
1396 list_response("PermissionSetsProvisioningStatus", rows, &b)
1397 }
1398
1399 fn application<'a>(
1402 &self,
1403 guard: &'a fakecloud_core::multi_account::MultiAccountState<crate::state::SsoAdminData>,
1404 account: &str,
1405 arn: &str,
1406 ) -> Option<&'a StoredApplication> {
1407 guard.get(account).and_then(|a| a.applications.get(arn))
1408 }
1409
1410 fn create_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1411 let b = parse(req)?;
1412 validate_common(&b)?;
1413 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
1414 let provider_arn = req_str(&b, "ApplicationProviderArn")?.to_string();
1415 let name = req_str(&b, "Name")?.to_string();
1416 check_len(&b, "Name", 1, 100)?;
1417 check_len(&b, "Description", 1, 128)?;
1418 let mut guard = self.state.write();
1419 let acct = guard.get_or_create(&req.account_id);
1420 let inst = acct
1421 .instances
1422 .get(&instance_arn)
1423 .ok_or_else(|| not_found("Instance not found."))?;
1424 let ssoins = inst.ssoins.clone();
1425 let identity_store_id = inst.identity_store_id.clone();
1426 let arn = format!(
1427 "arn:aws:sso::{}:application/{ssoins}/apl-{}",
1428 req.account_id,
1429 hex16()
1430 );
1431 let identity_store_arn = format!(
1432 "arn:aws:identitystore::{}:identitystore/{identity_store_id}",
1433 req.account_id
1434 );
1435 let app = StoredApplication {
1436 arn: arn.clone(),
1437 provider_arn,
1438 name: Some(name),
1439 account: req.account_id.clone(),
1440 instance_arn: instance_arn.clone(),
1441 identity_store_arn: identity_store_arn.clone(),
1442 status: Some(
1443 b.get("Status")
1444 .and_then(Value::as_str)
1445 .unwrap_or("ENABLED")
1446 .to_string(),
1447 ),
1448 portal_options: portal_options_with_defaults(b.get("PortalOptions").cloned()),
1449 description: b
1450 .get("Description")
1451 .and_then(Value::as_str)
1452 .map(str::to_string),
1453 created_date: now(),
1454 created_from: Some(req.region.clone()),
1455 assignments: Vec::new(),
1456 assignment_required: None,
1457 access_scopes: Default::default(),
1458 auth_methods: Default::default(),
1459 grants: Default::default(),
1460 session_status: None,
1461 };
1462 acct.applications.insert(arn.clone(), app);
1463 store_tags(acct, &arn, &b);
1464 ok(json!({
1465 "ApplicationArn": arn,
1466 "InstanceArn": instance_arn,
1467 "IdentityStoreArn": identity_store_arn,
1468 }))
1469 }
1470
1471 fn describe_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1472 let b = parse(req)?;
1473 validate_common(&b)?;
1474 let arn = req_str(&b, "ApplicationArn")?.to_string();
1475 let guard = self.state.read();
1476 let app = self
1477 .application(&guard, &req.account_id, &arn)
1478 .ok_or_else(|| not_found("Application not found."))?;
1479 ok(build_application(app))
1480 }
1481
1482 fn update_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1483 let b = parse(req)?;
1484 validate_common(&b)?;
1485 check_len(&b, "Name", 1, 100)?;
1486 check_len(&b, "Description", 1, 128)?;
1487 let arn = req_str(&b, "ApplicationArn")?.to_string();
1488 let mut guard = self.state.write();
1489 let app = guard
1490 .get_mut(&req.account_id)
1491 .and_then(|a| a.applications.get_mut(&arn))
1492 .ok_or_else(|| not_found("Application not found."))?;
1493 if let Some(n) = b.get("Name").and_then(Value::as_str) {
1494 app.name = Some(n.to_string());
1495 }
1496 if let Some(d) = b.get("Description").and_then(Value::as_str) {
1497 app.description = Some(d.to_string());
1498 }
1499 if let Some(s) = b.get("Status").and_then(Value::as_str) {
1500 app.status = Some(s.to_string());
1501 }
1502 if let Some(po) = b.get("PortalOptions") {
1503 app.portal_options = portal_options_with_defaults(Some(po.clone()));
1504 }
1505 ok(json!({}))
1506 }
1507
1508 fn delete_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1509 let b = parse(req)?;
1510 validate_common(&b)?;
1511 let arn = req_str(&b, "ApplicationArn")?.to_string();
1512 if let Some(a) = self.state.write().get_mut(&req.account_id) {
1513 a.applications.remove(&arn);
1514 }
1515 ok(json!({}))
1516 }
1517
1518 fn list_applications(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1519 let b = parse(req)?;
1520 validate_common(&b)?;
1521 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
1522 let guard = self.state.read();
1523 let rows: Vec<Value> = guard
1524 .get(&req.account_id)
1525 .map(|a| {
1526 a.applications
1527 .values()
1528 .filter(|app| app.instance_arn == instance_arn)
1529 .map(build_application)
1530 .collect()
1531 })
1532 .unwrap_or_default();
1533 list_response("Applications", rows, &b)
1534 }
1535
1536 fn with_application<F>(
1539 &self,
1540 req: &AwsRequest,
1541 b: &Value,
1542 f: F,
1543 ) -> Result<AwsResponse, AwsServiceError>
1544 where
1545 F: FnOnce(&mut StoredApplication) -> Result<AwsResponse, AwsServiceError>,
1546 {
1547 let arn = req_str(b, "ApplicationArn")?.to_string();
1548 let mut guard = self.state.write();
1549 let app = guard
1550 .get_mut(&req.account_id)
1551 .and_then(|a| a.applications.get_mut(&arn))
1552 .ok_or_else(|| not_found("Application not found."))?;
1553 f(app)
1554 }
1555
1556 fn create_application_assignment(
1557 &self,
1558 req: &AwsRequest,
1559 ) -> Result<AwsResponse, AwsServiceError> {
1560 let b = parse(req)?;
1561 validate_common(&b)?;
1562 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1563 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1564 self.with_application(req, &b, |app| {
1565 if !app
1566 .assignments
1567 .iter()
1568 .any(|(pid, pt)| pid == &principal_id && pt == &principal_type)
1569 {
1570 app.assignments.push((principal_id, principal_type));
1571 }
1572 ok(json!({}))
1573 })
1574 }
1575
1576 fn delete_application_assignment(
1577 &self,
1578 req: &AwsRequest,
1579 ) -> Result<AwsResponse, AwsServiceError> {
1580 let b = parse(req)?;
1581 validate_common(&b)?;
1582 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1583 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1584 self.with_application(req, &b, |app| {
1585 app.assignments
1586 .retain(|(pid, pt)| !(pid == &principal_id && pt == &principal_type));
1587 ok(json!({}))
1588 })
1589 }
1590
1591 fn describe_application_assignment(
1592 &self,
1593 req: &AwsRequest,
1594 ) -> Result<AwsResponse, AwsServiceError> {
1595 let b = parse(req)?;
1596 validate_common(&b)?;
1597 let arn = req_str(&b, "ApplicationArn")?.to_string();
1598 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1599 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1600 let guard = self.state.read();
1601 let app = self
1602 .application(&guard, &req.account_id, &arn)
1603 .ok_or_else(|| not_found("Application not found."))?;
1604 let found = app
1605 .assignments
1606 .iter()
1607 .any(|(pid, pt)| pid == &principal_id && pt == &principal_type);
1608 if !found {
1609 return Err(not_found("Application assignment not found."));
1610 }
1611 ok(json!({
1612 "ApplicationArn": arn,
1613 "PrincipalId": principal_id,
1614 "PrincipalType": principal_type,
1615 }))
1616 }
1617
1618 fn list_application_assignments(
1619 &self,
1620 req: &AwsRequest,
1621 ) -> Result<AwsResponse, AwsServiceError> {
1622 let b = parse(req)?;
1623 validate_common(&b)?;
1624 let arn = req_str(&b, "ApplicationArn")?.to_string();
1625 let guard = self.state.read();
1626 let rows: Vec<Value> = self
1627 .application(&guard, &req.account_id, &arn)
1628 .map(|app| {
1629 app.assignments
1630 .iter()
1631 .map(|(pid, pt)| {
1632 json!({ "ApplicationArn": arn, "PrincipalId": pid, "PrincipalType": pt })
1633 })
1634 .collect()
1635 })
1636 .unwrap_or_default();
1637 list_response("ApplicationAssignments", rows, &b)
1638 }
1639
1640 fn list_application_assignments_for_principal(
1641 &self,
1642 req: &AwsRequest,
1643 ) -> Result<AwsResponse, AwsServiceError> {
1644 let b = parse(req)?;
1645 validate_common(&b)?;
1646 req_str(&b, "InstanceArn")?;
1647 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1648 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1649 let guard = self.state.read();
1650 let rows: Vec<Value> = guard
1651 .get(&req.account_id)
1652 .map(|a| {
1653 a.applications
1654 .values()
1655 .flat_map(|app| {
1656 app.assignments
1657 .iter()
1658 .filter(|(pid, pt)| pid == &principal_id && pt == &principal_type)
1659 .map(|(pid, pt)| {
1660 json!({
1661 "ApplicationArn": app.arn,
1662 "PrincipalId": pid,
1663 "PrincipalType": pt,
1664 })
1665 })
1666 .collect::<Vec<_>>()
1667 })
1668 .collect()
1669 })
1670 .unwrap_or_default();
1671 list_response("ApplicationAssignments", rows, &b)
1672 }
1673
1674 fn get_application_assignment_configuration(
1675 &self,
1676 req: &AwsRequest,
1677 ) -> Result<AwsResponse, AwsServiceError> {
1678 let b = parse(req)?;
1679 validate_common(&b)?;
1680 let arn = req_str(&b, "ApplicationArn")?.to_string();
1681 let guard = self.state.read();
1682 let app = self
1683 .application(&guard, &req.account_id, &arn)
1684 .ok_or_else(|| not_found("Application not found."))?;
1685 ok(json!({ "AssignmentRequired": app.assignment_required.unwrap_or(true) }))
1687 }
1688
1689 fn put_application_assignment_configuration(
1690 &self,
1691 req: &AwsRequest,
1692 ) -> Result<AwsResponse, AwsServiceError> {
1693 let b = parse(req)?;
1694 validate_common(&b)?;
1695 let required = b
1696 .get("AssignmentRequired")
1697 .and_then(Value::as_bool)
1698 .ok_or_else(|| validation("AssignmentRequired must be specified."))?;
1699 self.with_application(req, &b, |app| {
1700 app.assignment_required = Some(required);
1701 ok(json!({}))
1702 })
1703 }
1704
1705 fn put_application_access_scope(
1708 &self,
1709 req: &AwsRequest,
1710 ) -> Result<AwsResponse, AwsServiceError> {
1711 let b = parse(req)?;
1712 validate_common(&b)?;
1713 let scope = req_str(&b, "Scope")?.to_string();
1714 let targets = b.get("AuthorizedTargets").cloned();
1715 self.with_application(req, &b, |app| {
1716 app.access_scopes
1717 .insert(scope, targets.unwrap_or(Value::Null));
1718 ok(json!({}))
1719 })
1720 }
1721
1722 fn get_application_access_scope(
1723 &self,
1724 req: &AwsRequest,
1725 ) -> Result<AwsResponse, AwsServiceError> {
1726 let b = parse(req)?;
1727 validate_common(&b)?;
1728 let arn = req_str(&b, "ApplicationArn")?.to_string();
1729 let scope = req_str(&b, "Scope")?.to_string();
1730 let guard = self.state.read();
1731 let app = self
1732 .application(&guard, &req.account_id, &arn)
1733 .ok_or_else(|| not_found("Application not found."))?;
1734 let targets = app
1735 .access_scopes
1736 .get(&scope)
1737 .ok_or_else(|| not_found("Access scope not found."))?;
1738 let mut m = serde_json::Map::new();
1739 m.insert("Scope".into(), json!(scope));
1740 if !targets.is_null() {
1741 m.insert("AuthorizedTargets".into(), targets.clone());
1742 }
1743 ok(Value::Object(m))
1744 }
1745
1746 fn delete_application_access_scope(
1747 &self,
1748 req: &AwsRequest,
1749 ) -> Result<AwsResponse, AwsServiceError> {
1750 let b = parse(req)?;
1751 validate_common(&b)?;
1752 let scope = req_str(&b, "Scope")?.to_string();
1753 self.with_application(req, &b, |app| {
1754 app.access_scopes.remove(&scope);
1755 ok(json!({}))
1756 })
1757 }
1758
1759 fn list_application_access_scopes(
1760 &self,
1761 req: &AwsRequest,
1762 ) -> Result<AwsResponse, AwsServiceError> {
1763 let b = parse(req)?;
1764 validate_common(&b)?;
1765 let arn = req_str(&b, "ApplicationArn")?.to_string();
1766 let guard = self.state.read();
1767 let rows: Vec<Value> = self
1768 .application(&guard, &req.account_id, &arn)
1769 .map(|app| {
1770 app.access_scopes
1771 .iter()
1772 .map(|(scope, targets)| {
1773 let mut m = serde_json::Map::new();
1774 m.insert("Scope".into(), json!(scope));
1775 if !targets.is_null() {
1776 m.insert("AuthorizedTargets".into(), targets.clone());
1777 }
1778 Value::Object(m)
1779 })
1780 .collect()
1781 })
1782 .unwrap_or_default();
1783 list_response("Scopes", rows, &b)
1784 }
1785
1786 fn put_application_authentication_method(
1789 &self,
1790 req: &AwsRequest,
1791 ) -> Result<AwsResponse, AwsServiceError> {
1792 let b = parse(req)?;
1793 validate_common(&b)?;
1794 let mtype = req_str(&b, "AuthenticationMethodType")?.to_string();
1795 let method = b
1796 .get("AuthenticationMethod")
1797 .cloned()
1798 .ok_or_else(|| validation("AuthenticationMethod must be specified."))?;
1799 self.with_application(req, &b, |app| {
1800 app.auth_methods.insert(mtype, method);
1801 ok(json!({}))
1802 })
1803 }
1804
1805 fn get_application_authentication_method(
1806 &self,
1807 req: &AwsRequest,
1808 ) -> Result<AwsResponse, AwsServiceError> {
1809 let b = parse(req)?;
1810 validate_common(&b)?;
1811 let arn = req_str(&b, "ApplicationArn")?.to_string();
1812 let mtype = req_str(&b, "AuthenticationMethodType")?.to_string();
1813 let guard = self.state.read();
1814 let app = self
1815 .application(&guard, &req.account_id, &arn)
1816 .ok_or_else(|| not_found("Application not found."))?;
1817 let method = app
1818 .auth_methods
1819 .get(&mtype)
1820 .ok_or_else(|| not_found("Authentication method not found."))?;
1821 ok(json!({ "AuthenticationMethod": method }))
1822 }
1823
1824 fn delete_application_authentication_method(
1825 &self,
1826 req: &AwsRequest,
1827 ) -> Result<AwsResponse, AwsServiceError> {
1828 let b = parse(req)?;
1829 validate_common(&b)?;
1830 let mtype = req_str(&b, "AuthenticationMethodType")?.to_string();
1831 self.with_application(req, &b, |app| {
1832 app.auth_methods.remove(&mtype);
1833 ok(json!({}))
1834 })
1835 }
1836
1837 fn list_application_authentication_methods(
1838 &self,
1839 req: &AwsRequest,
1840 ) -> Result<AwsResponse, AwsServiceError> {
1841 let b = parse(req)?;
1842 validate_common(&b)?;
1843 let arn = req_str(&b, "ApplicationArn")?.to_string();
1844 let guard = self.state.read();
1845 let rows: Vec<Value> = self
1846 .application(&guard, &req.account_id, &arn)
1847 .map(|app| {
1848 app.auth_methods
1849 .iter()
1850 .map(|(t, m)| json!({ "AuthenticationMethodType": t, "AuthenticationMethod": m }))
1851 .collect()
1852 })
1853 .unwrap_or_default();
1854 list_response("AuthenticationMethods", rows, &b)
1855 }
1856
1857 fn put_application_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1860 let b = parse(req)?;
1861 validate_common(&b)?;
1862 let gtype = req_str(&b, "GrantType")?.to_string();
1863 let grant = b
1864 .get("Grant")
1865 .cloned()
1866 .ok_or_else(|| validation("Grant must be specified."))?;
1867 self.with_application(req, &b, |app| {
1868 app.grants.insert(gtype, grant);
1869 ok(json!({}))
1870 })
1871 }
1872
1873 fn get_application_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1874 let b = parse(req)?;
1875 validate_common(&b)?;
1876 let arn = req_str(&b, "ApplicationArn")?.to_string();
1877 let gtype = req_str(&b, "GrantType")?.to_string();
1878 let guard = self.state.read();
1879 let app = self
1880 .application(&guard, &req.account_id, &arn)
1881 .ok_or_else(|| not_found("Application not found."))?;
1882 let grant = app
1883 .grants
1884 .get(>ype)
1885 .ok_or_else(|| not_found("Grant not found."))?;
1886 ok(json!({ "Grant": grant }))
1887 }
1888
1889 fn delete_application_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1890 let b = parse(req)?;
1891 validate_common(&b)?;
1892 let gtype = req_str(&b, "GrantType")?.to_string();
1893 self.with_application(req, &b, |app| {
1894 app.grants.remove(>ype);
1895 ok(json!({}))
1896 })
1897 }
1898
1899 fn list_application_grants(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1900 let b = parse(req)?;
1901 validate_common(&b)?;
1902 let arn = req_str(&b, "ApplicationArn")?.to_string();
1903 let guard = self.state.read();
1904 let rows: Vec<Value> = self
1905 .application(&guard, &req.account_id, &arn)
1906 .map(|app| {
1907 app.grants
1908 .iter()
1909 .map(|(t, g)| json!({ "GrantType": t, "Grant": g }))
1910 .collect()
1911 })
1912 .unwrap_or_default();
1913 list_response("Grants", rows, &b)
1914 }
1915
1916 fn get_application_session_configuration(
1919 &self,
1920 req: &AwsRequest,
1921 ) -> Result<AwsResponse, AwsServiceError> {
1922 let b = parse(req)?;
1923 validate_common(&b)?;
1924 let arn = req_str(&b, "ApplicationArn")?.to_string();
1925 let guard = self.state.read();
1926 let app = self
1927 .application(&guard, &req.account_id, &arn)
1928 .ok_or_else(|| not_found("Application not found."))?;
1929 let mut m = serde_json::Map::new();
1930 if let Some(status) = &app.session_status {
1931 m.insert(
1932 "UserBackgroundSessionApplicationStatus".into(),
1933 json!(status),
1934 );
1935 }
1936 ok(Value::Object(m))
1937 }
1938
1939 fn put_application_session_configuration(
1940 &self,
1941 req: &AwsRequest,
1942 ) -> Result<AwsResponse, AwsServiceError> {
1943 let b = parse(req)?;
1944 validate_common(&b)?;
1945 let status = b
1946 .get("UserBackgroundSessionApplicationStatus")
1947 .and_then(Value::as_str)
1948 .map(str::to_string);
1949 self.with_application(req, &b, |app| {
1950 app.session_status = status;
1951 ok(json!({}))
1952 })
1953 }
1954
1955 fn list_application_providers(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1958 let b = parse(req)?;
1959 validate_common(&b)?;
1960 let rows: Vec<Value> = APPLICATION_PROVIDERS
1961 .iter()
1962 .map(|(arn, proto, display)| {
1963 json!({
1964 "ApplicationProviderArn": arn,
1965 "FederationProtocol": proto,
1966 "DisplayData": { "DisplayName": display },
1967 })
1968 })
1969 .collect();
1970 list_response("ApplicationProviders", rows, &b)
1971 }
1972
1973 fn describe_application_provider(
1974 &self,
1975 req: &AwsRequest,
1976 ) -> Result<AwsResponse, AwsServiceError> {
1977 let b = parse(req)?;
1978 validate_common(&b)?;
1979 let arn = req_str(&b, "ApplicationProviderArn")?.to_string();
1980 let found = APPLICATION_PROVIDERS
1981 .iter()
1982 .find(|(a, _, _)| *a == arn)
1983 .ok_or_else(|| not_found("Application provider not found."))?;
1984 ok(json!({
1985 "ApplicationProviderArn": found.0,
1986 "FederationProtocol": found.1,
1987 "DisplayData": { "DisplayName": found.2 },
1988 }))
1989 }
1990
1991 fn create_trusted_token_issuer(
1994 &self,
1995 req: &AwsRequest,
1996 ) -> Result<AwsResponse, AwsServiceError> {
1997 let b = parse(req)?;
1998 validate_common(&b)?;
1999 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2000 let name = req_str(&b, "Name")?.to_string();
2001 check_len(&b, "Name", 1, 255)?;
2002 req_str(&b, "TrustedTokenIssuerType")?;
2003 let guard = self.state.read();
2006 let ssoins = guard
2007 .get(&req.account_id)
2008 .and_then(|a| a.instances.get(&instance_arn))
2009 .map(|i| i.ssoins.clone())
2010 .unwrap_or_else(|| format!("ssoins-{}", hex16()));
2011 drop(guard);
2012 let arn = format!(
2013 "arn:aws:sso::{}:trustedTokenIssuer/{ssoins}/tti-{}",
2014 req.account_id,
2015 Uuid::new_v4()
2016 );
2017 let tti = StoredTrustedTokenIssuer {
2018 arn: arn.clone(),
2019 instance_arn,
2020 name: Some(name),
2021 issuer_type: b
2022 .get("TrustedTokenIssuerType")
2023 .and_then(Value::as_str)
2024 .unwrap_or("OIDC_JWT")
2025 .to_string(),
2026 config: b.get("TrustedTokenIssuerConfiguration").cloned(),
2027 };
2028 {
2029 let mut guard = self.state.write();
2030 let acct = guard.get_or_create(&req.account_id);
2031 acct.trusted_token_issuers.insert(arn.clone(), tti);
2032 store_tags(acct, &arn, &b);
2033 }
2034 ok(json!({ "TrustedTokenIssuerArn": arn }))
2035 }
2036
2037 fn describe_trusted_token_issuer(
2038 &self,
2039 req: &AwsRequest,
2040 ) -> Result<AwsResponse, AwsServiceError> {
2041 let b = parse(req)?;
2042 validate_common(&b)?;
2043 let arn = req_str(&b, "TrustedTokenIssuerArn")?.to_string();
2044 let guard = self.state.read();
2045 let tti = guard
2046 .get(&req.account_id)
2047 .and_then(|a| a.trusted_token_issuers.get(&arn))
2048 .ok_or_else(|| not_found("Trusted token issuer not found."))?;
2049 ok(build_tti_describe(tti))
2050 }
2051
2052 fn update_trusted_token_issuer(
2053 &self,
2054 req: &AwsRequest,
2055 ) -> Result<AwsResponse, AwsServiceError> {
2056 let b = parse(req)?;
2057 validate_common(&b)?;
2058 check_len(&b, "Name", 1, 255)?;
2059 let arn = req_str(&b, "TrustedTokenIssuerArn")?.to_string();
2060 let mut guard = self.state.write();
2061 let tti = guard
2062 .get_mut(&req.account_id)
2063 .and_then(|a| a.trusted_token_issuers.get_mut(&arn))
2064 .ok_or_else(|| not_found("Trusted token issuer not found."))?;
2065 if let Some(n) = b.get("Name").and_then(Value::as_str) {
2066 tti.name = Some(n.to_string());
2067 }
2068 if let Some(c) = b.get("TrustedTokenIssuerConfiguration") {
2069 match &mut tti.config {
2073 Some(existing) => deep_merge(existing, c),
2074 None => tti.config = Some(c.clone()),
2075 }
2076 }
2077 ok(json!({}))
2078 }
2079
2080 fn delete_trusted_token_issuer(
2081 &self,
2082 req: &AwsRequest,
2083 ) -> Result<AwsResponse, AwsServiceError> {
2084 let b = parse(req)?;
2085 validate_common(&b)?;
2086 let arn = req_str(&b, "TrustedTokenIssuerArn")?.to_string();
2087 if let Some(a) = self.state.write().get_mut(&req.account_id) {
2088 a.trusted_token_issuers.remove(&arn);
2089 }
2090 ok(json!({}))
2091 }
2092
2093 fn list_trusted_token_issuers(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2094 let b = parse(req)?;
2095 validate_common(&b)?;
2096 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2097 let guard = self.state.read();
2098 let rows: Vec<Value> = guard
2099 .get(&req.account_id)
2100 .map(|a| {
2101 a.trusted_token_issuers
2102 .values()
2103 .filter(|t| t.instance_arn == instance_arn)
2104 .map(build_tti_metadata)
2105 .collect()
2106 })
2107 .unwrap_or_default();
2108 list_response("TrustedTokenIssuers", rows, &b)
2109 }
2110
2111 fn add_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2114 let b = parse(req)?;
2115 validate_common(&b)?;
2116 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2117 let region_name = req_str(&b, "RegionName")?.to_string();
2118 let mut guard = self.state.write();
2119 let acct = guard.get_or_create(&req.account_id);
2120 let ssoins = instance_arn
2123 .rsplit('/')
2124 .next()
2125 .unwrap_or("ssoins-unknown")
2126 .to_string();
2127 let inst = acct
2128 .instances
2129 .entry(instance_arn.clone())
2130 .or_insert_with(|| StoredInstance {
2131 arn: instance_arn.clone(),
2132 ssoins,
2133 identity_store_id: format!("d-{}", hex10()),
2134 owner_account_id: req.account_id.clone(),
2135 name: None,
2136 status: "ACTIVE".into(),
2137 created_date: now(),
2138 regions: Vec::new(),
2139 primary_region: Some(req.region.clone()),
2140 attr_config: None,
2141 attr_config_status: None,
2142 });
2143 if !inst.regions.iter().any(|r| r.region_name == region_name) {
2144 let is_primary = inst.regions.is_empty();
2145 if is_primary {
2146 inst.primary_region = Some(region_name.clone());
2147 }
2148 inst.regions.push(StoredRegion {
2149 region_name,
2150 status: "ACTIVE".into(),
2151 added_date: now(),
2152 is_primary,
2153 });
2154 }
2155 ok(json!({ "Status": "ACTIVE" }))
2156 }
2157
2158 fn remove_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2159 let b = parse(req)?;
2160 validate_common(&b)?;
2161 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2162 let region_name = req_str(&b, "RegionName")?.to_string();
2163 if let Some(inst) = self
2164 .state
2165 .write()
2166 .get_mut(&req.account_id)
2167 .and_then(|a| a.instances.get_mut(&instance_arn))
2168 {
2169 inst.regions.retain(|r| r.region_name != region_name);
2170 }
2171 ok(json!({ "Status": "REMOVING" }))
2172 }
2173
2174 fn describe_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2175 let b = parse(req)?;
2176 validate_common(&b)?;
2177 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2178 let region_name = req_str(&b, "RegionName")?.to_string();
2179 let guard = self.state.read();
2180 let region = guard
2181 .get(&req.account_id)
2182 .and_then(|a| a.instances.get(&instance_arn))
2183 .and_then(|i| i.regions.iter().find(|r| r.region_name == region_name))
2184 .ok_or_else(|| not_found("Region not found."))?;
2185 ok(build_region(region))
2186 }
2187
2188 fn list_regions(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2189 let b = parse(req)?;
2190 validate_common(&b)?;
2191 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2192 let guard = self.state.read();
2193 let rows: Vec<Value> = guard
2194 .get(&req.account_id)
2195 .and_then(|a| a.instances.get(&instance_arn))
2196 .map(|i| i.regions.iter().map(build_region).collect())
2197 .unwrap_or_default();
2198 list_response("Regions", rows, &b)
2199 }
2200
2201 fn create_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2204 let b = parse(req)?;
2205 validate_common(&b)?;
2206 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2207 let config = b
2208 .get("InstanceAccessControlAttributeConfiguration")
2209 .cloned()
2210 .ok_or_else(|| {
2211 validation("InstanceAccessControlAttributeConfiguration must be specified.")
2212 })?;
2213 let mut guard = self.state.write();
2214 let inst = guard
2215 .get_mut(&req.account_id)
2216 .and_then(|a| a.instances.get_mut(&instance_arn))
2217 .ok_or_else(|| not_found("Instance not found."))?;
2218 inst.attr_config = Some(config);
2219 inst.attr_config_status = Some("ENABLED".into());
2220 ok(json!({}))
2221 }
2222
2223 fn describe_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2224 let b = parse(req)?;
2225 validate_common(&b)?;
2226 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2227 let guard = self.state.read();
2228 let inst = guard
2229 .get(&req.account_id)
2230 .and_then(|a| a.instances.get(&instance_arn))
2231 .ok_or_else(|| not_found("Instance not found."))?;
2232 let config = inst
2233 .attr_config
2234 .clone()
2235 .ok_or_else(|| not_found("Access control attribute configuration not found."))?;
2236 ok(json!({
2237 "Status": inst.attr_config_status.clone().unwrap_or_else(|| "ENABLED".into()),
2238 "InstanceAccessControlAttributeConfiguration": config,
2239 }))
2240 }
2241
2242 fn update_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2243 let b = parse(req)?;
2244 validate_common(&b)?;
2245 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2246 let config = b
2247 .get("InstanceAccessControlAttributeConfiguration")
2248 .cloned()
2249 .ok_or_else(|| {
2250 validation("InstanceAccessControlAttributeConfiguration must be specified.")
2251 })?;
2252 let mut guard = self.state.write();
2253 let inst = guard
2254 .get_mut(&req.account_id)
2255 .and_then(|a| a.instances.get_mut(&instance_arn))
2256 .ok_or_else(|| not_found("Instance not found."))?;
2257 inst.attr_config = Some(config);
2258 inst.attr_config_status = Some("ENABLED".into());
2259 ok(json!({}))
2260 }
2261
2262 fn delete_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2263 let b = parse(req)?;
2264 validate_common(&b)?;
2265 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2266 if let Some(inst) = self
2267 .state
2268 .write()
2269 .get_mut(&req.account_id)
2270 .and_then(|a| a.instances.get_mut(&instance_arn))
2271 {
2272 inst.attr_config = None;
2273 inst.attr_config_status = None;
2274 }
2275 ok(json!({}))
2276 }
2277
2278 fn tag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2281 let b = parse(req)?;
2282 validate_common(&b)?;
2283 let resource_arn = req_str(&b, "ResourceArn")?.to_string();
2284 let tags = b
2285 .get("Tags")
2286 .and_then(Value::as_array)
2287 .ok_or_else(|| validation("Tags must be specified."))?;
2288 let mut guard = self.state.write();
2289 let entry = guard
2290 .get_or_create(&req.account_id)
2291 .tags
2292 .entry(resource_arn)
2293 .or_default();
2294 for tag in tags {
2295 if let (Some(k), Some(v)) = (
2296 tag.get("Key").and_then(Value::as_str),
2297 tag.get("Value").and_then(Value::as_str),
2298 ) {
2299 entry.insert(k.to_string(), v.to_string());
2300 }
2301 }
2302 ok(json!({}))
2303 }
2304
2305 fn untag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2306 let b = parse(req)?;
2307 validate_common(&b)?;
2308 let resource_arn = req_str(&b, "ResourceArn")?.to_string();
2309 let keys = b
2310 .get("TagKeys")
2311 .and_then(Value::as_array)
2312 .ok_or_else(|| validation("TagKeys must be specified."))?;
2313 if let Some(entry) = self
2314 .state
2315 .write()
2316 .get_mut(&req.account_id)
2317 .and_then(|a| a.tags.get_mut(&resource_arn))
2318 {
2319 for k in keys {
2320 if let Some(k) = k.as_str() {
2321 entry.remove(k);
2322 }
2323 }
2324 }
2325 ok(json!({}))
2326 }
2327
2328 fn list_tags_for_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2329 let b = parse(req)?;
2330 validate_common(&b)?;
2331 let resource_arn = req_str(&b, "ResourceArn")?.to_string();
2332 let guard = self.state.read();
2333 let rows: Vec<Value> = guard
2334 .get(&req.account_id)
2335 .and_then(|a| a.tags.get(&resource_arn))
2336 .map(|m| {
2337 m.iter()
2338 .map(|(k, v)| json!({ "Key": k, "Value": v }))
2339 .collect()
2340 })
2341 .unwrap_or_default();
2342 list_response("Tags", rows, &b)
2343 }
2344}
2345
2346#[cfg(test)]
2347mod tests;