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 let principal_type = req_str(&b, "PrincipalType")?.to_string();
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| {
1275 x.principal_id == principal_id && x.principal_type == principal_type
1276 })
1277 .map(|x| {
1278 json!({
1279 "AccountId": x.account_id,
1280 "PermissionSetArn": x.permission_set_arn,
1281 "PrincipalId": x.principal_id,
1282 "PrincipalType": x.principal_type,
1283 })
1284 })
1285 .collect()
1286 })
1287 .unwrap_or_default();
1288 list_response("AccountAssignments", rows, &b)
1289 }
1290
1291 fn list_accounts_for_provisioned(
1292 &self,
1293 req: &AwsRequest,
1294 ) -> Result<AwsResponse, AwsServiceError> {
1295 let b = parse(req)?;
1296 validate_common(&b)?;
1297 req_str(&b, "InstanceArn")?;
1298 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1299 let guard = self.state.read();
1300 let mut accounts: Vec<String> = guard
1301 .get(&req.account_id)
1302 .map(|a| {
1303 a.assignments
1304 .iter()
1305 .filter(|x| x.permission_set_arn == ps_arn)
1306 .map(|x| x.account_id.clone())
1307 .collect()
1308 })
1309 .unwrap_or_default();
1310 accounts.sort();
1311 accounts.dedup();
1312 let rows: Vec<Value> = accounts.into_iter().map(|a| json!(a)).collect();
1313 list_response("AccountIds", rows, &b)
1314 }
1315
1316 fn list_ps_provisioned_to_account(
1317 &self,
1318 req: &AwsRequest,
1319 ) -> Result<AwsResponse, AwsServiceError> {
1320 let b = parse(req)?;
1321 validate_common(&b)?;
1322 req_str(&b, "InstanceArn")?;
1323 let account_id = req_str(&b, "AccountId")?.to_string();
1324 let guard = self.state.read();
1325 let mut ps: Vec<String> = guard
1326 .get(&req.account_id)
1327 .map(|a| {
1328 a.assignments
1329 .iter()
1330 .filter(|x| x.account_id == account_id)
1331 .map(|x| x.permission_set_arn.clone())
1332 .collect()
1333 })
1334 .unwrap_or_default();
1335 ps.sort();
1336 ps.dedup();
1337 let rows: Vec<Value> = ps.into_iter().map(|a| json!(a)).collect();
1338 list_response("PermissionSets", rows, &b)
1339 }
1340
1341 fn provision_permission_set(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1344 let b = parse(req)?;
1345 validate_common(&b)?;
1346 req_str(&b, "InstanceArn")?;
1347 let ps_arn = req_str(&b, "PermissionSetArn")?.to_string();
1348 req_str(&b, "TargetType")?;
1349 let request_id = Uuid::new_v4().to_string();
1350 let st = StoredProvisioningStatus {
1351 request_id: request_id.clone(),
1352 status: "IN_PROGRESS".into(),
1353 account_id: b
1354 .get("TargetId")
1355 .and_then(Value::as_str)
1356 .map(str::to_string),
1357 permission_set_arn: ps_arn,
1358 created_date: now(),
1359 };
1360 self.state
1361 .write()
1362 .get_or_create(&req.account_id)
1363 .ps_provisioning
1364 .insert(request_id, st.clone());
1365 ok(json!({ "PermissionSetProvisioningStatus": build_prov_status(&st) }))
1366 }
1367
1368 fn describe_ps_provisioning_status(
1369 &self,
1370 req: &AwsRequest,
1371 ) -> Result<AwsResponse, AwsServiceError> {
1372 let b = parse(req)?;
1373 validate_common(&b)?;
1374 req_str(&b, "InstanceArn")?;
1375 let id = req_str(&b, "ProvisionPermissionSetRequestId")?.to_string();
1376 let mut guard = self.state.write();
1377 let st = guard
1378 .get_mut(&req.account_id)
1379 .and_then(|a| a.ps_provisioning.get_mut(&id))
1380 .ok_or_else(|| not_found("Provisioning status not found."))?;
1381 st.status = "SUCCEEDED".into();
1382 ok(json!({ "PermissionSetProvisioningStatus": build_prov_status(st) }))
1383 }
1384
1385 fn list_ps_provisioning_status(
1386 &self,
1387 req: &AwsRequest,
1388 ) -> Result<AwsResponse, AwsServiceError> {
1389 let b = parse(req)?;
1390 validate_common(&b)?;
1391 req_str(&b, "InstanceArn")?;
1392 let guard = self.state.read();
1393 let rows: Vec<Value> = guard
1394 .get(&req.account_id)
1395 .map(|a| {
1396 a.ps_provisioning
1397 .values()
1398 .map(|st| json!({ "Status": st.status, "RequestId": st.request_id, "CreatedDate": st.created_date }))
1399 .collect()
1400 })
1401 .unwrap_or_default();
1402 list_response("PermissionSetsProvisioningStatus", rows, &b)
1403 }
1404
1405 fn application<'a>(
1408 &self,
1409 guard: &'a fakecloud_core::multi_account::MultiAccountState<crate::state::SsoAdminData>,
1410 account: &str,
1411 arn: &str,
1412 ) -> Option<&'a StoredApplication> {
1413 guard.get(account).and_then(|a| a.applications.get(arn))
1414 }
1415
1416 fn create_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1417 let b = parse(req)?;
1418 validate_common(&b)?;
1419 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
1420 let provider_arn = req_str(&b, "ApplicationProviderArn")?.to_string();
1421 let name = req_str(&b, "Name")?.to_string();
1422 check_len(&b, "Name", 1, 100)?;
1423 check_len(&b, "Description", 1, 128)?;
1424 let mut guard = self.state.write();
1425 let acct = guard.get_or_create(&req.account_id);
1426 let inst = acct
1427 .instances
1428 .get(&instance_arn)
1429 .ok_or_else(|| not_found("Instance not found."))?;
1430 let ssoins = inst.ssoins.clone();
1431 let identity_store_id = inst.identity_store_id.clone();
1432 let arn = format!(
1433 "arn:aws:sso::{}:application/{ssoins}/apl-{}",
1434 req.account_id,
1435 hex16()
1436 );
1437 let identity_store_arn = format!(
1438 "arn:aws:identitystore::{}:identitystore/{identity_store_id}",
1439 req.account_id
1440 );
1441 let app = StoredApplication {
1442 arn: arn.clone(),
1443 provider_arn,
1444 name: Some(name),
1445 account: req.account_id.clone(),
1446 instance_arn: instance_arn.clone(),
1447 identity_store_arn: identity_store_arn.clone(),
1448 status: Some(
1449 b.get("Status")
1450 .and_then(Value::as_str)
1451 .unwrap_or("ENABLED")
1452 .to_string(),
1453 ),
1454 portal_options: portal_options_with_defaults(b.get("PortalOptions").cloned()),
1455 description: b
1456 .get("Description")
1457 .and_then(Value::as_str)
1458 .map(str::to_string),
1459 created_date: now(),
1460 created_from: Some(req.region.clone()),
1461 assignments: Vec::new(),
1462 assignment_required: None,
1463 access_scopes: Default::default(),
1464 auth_methods: Default::default(),
1465 grants: Default::default(),
1466 session_status: None,
1467 };
1468 acct.applications.insert(arn.clone(), app);
1469 store_tags(acct, &arn, &b);
1470 ok(json!({
1471 "ApplicationArn": arn,
1472 "InstanceArn": instance_arn,
1473 "IdentityStoreArn": identity_store_arn,
1474 }))
1475 }
1476
1477 fn describe_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1478 let b = parse(req)?;
1479 validate_common(&b)?;
1480 let arn = req_str(&b, "ApplicationArn")?.to_string();
1481 let guard = self.state.read();
1482 let app = self
1483 .application(&guard, &req.account_id, &arn)
1484 .ok_or_else(|| not_found("Application not found."))?;
1485 ok(build_application(app))
1486 }
1487
1488 fn update_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1489 let b = parse(req)?;
1490 validate_common(&b)?;
1491 check_len(&b, "Name", 1, 100)?;
1492 check_len(&b, "Description", 1, 128)?;
1493 let arn = req_str(&b, "ApplicationArn")?.to_string();
1494 let mut guard = self.state.write();
1495 let app = guard
1496 .get_mut(&req.account_id)
1497 .and_then(|a| a.applications.get_mut(&arn))
1498 .ok_or_else(|| not_found("Application not found."))?;
1499 if let Some(n) = b.get("Name").and_then(Value::as_str) {
1500 app.name = Some(n.to_string());
1501 }
1502 if let Some(d) = b.get("Description").and_then(Value::as_str) {
1503 app.description = Some(d.to_string());
1504 }
1505 if let Some(s) = b.get("Status").and_then(Value::as_str) {
1506 app.status = Some(s.to_string());
1507 }
1508 if let Some(po) = b.get("PortalOptions") {
1509 app.portal_options = portal_options_with_defaults(Some(po.clone()));
1510 }
1511 ok(json!({}))
1512 }
1513
1514 fn delete_application(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1515 let b = parse(req)?;
1516 validate_common(&b)?;
1517 let arn = req_str(&b, "ApplicationArn")?.to_string();
1518 if let Some(a) = self.state.write().get_mut(&req.account_id) {
1519 a.applications.remove(&arn);
1520 }
1521 ok(json!({}))
1522 }
1523
1524 fn list_applications(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1525 let b = parse(req)?;
1526 validate_common(&b)?;
1527 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
1528 let guard = self.state.read();
1529 let rows: Vec<Value> = guard
1530 .get(&req.account_id)
1531 .map(|a| {
1532 a.applications
1533 .values()
1534 .filter(|app| app.instance_arn == instance_arn)
1535 .map(build_application)
1536 .collect()
1537 })
1538 .unwrap_or_default();
1539 list_response("Applications", rows, &b)
1540 }
1541
1542 fn with_application<F>(
1545 &self,
1546 req: &AwsRequest,
1547 b: &Value,
1548 f: F,
1549 ) -> Result<AwsResponse, AwsServiceError>
1550 where
1551 F: FnOnce(&mut StoredApplication) -> Result<AwsResponse, AwsServiceError>,
1552 {
1553 let arn = req_str(b, "ApplicationArn")?.to_string();
1554 let mut guard = self.state.write();
1555 let app = guard
1556 .get_mut(&req.account_id)
1557 .and_then(|a| a.applications.get_mut(&arn))
1558 .ok_or_else(|| not_found("Application not found."))?;
1559 f(app)
1560 }
1561
1562 fn create_application_assignment(
1563 &self,
1564 req: &AwsRequest,
1565 ) -> Result<AwsResponse, AwsServiceError> {
1566 let b = parse(req)?;
1567 validate_common(&b)?;
1568 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1569 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1570 self.with_application(req, &b, |app| {
1571 if !app
1572 .assignments
1573 .iter()
1574 .any(|(pid, pt)| pid == &principal_id && pt == &principal_type)
1575 {
1576 app.assignments.push((principal_id, principal_type));
1577 }
1578 ok(json!({}))
1579 })
1580 }
1581
1582 fn delete_application_assignment(
1583 &self,
1584 req: &AwsRequest,
1585 ) -> Result<AwsResponse, AwsServiceError> {
1586 let b = parse(req)?;
1587 validate_common(&b)?;
1588 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1589 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1590 self.with_application(req, &b, |app| {
1591 app.assignments
1592 .retain(|(pid, pt)| !(pid == &principal_id && pt == &principal_type));
1593 ok(json!({}))
1594 })
1595 }
1596
1597 fn describe_application_assignment(
1598 &self,
1599 req: &AwsRequest,
1600 ) -> Result<AwsResponse, AwsServiceError> {
1601 let b = parse(req)?;
1602 validate_common(&b)?;
1603 let arn = req_str(&b, "ApplicationArn")?.to_string();
1604 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1605 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1606 let guard = self.state.read();
1607 let app = self
1608 .application(&guard, &req.account_id, &arn)
1609 .ok_or_else(|| not_found("Application not found."))?;
1610 let found = app
1611 .assignments
1612 .iter()
1613 .any(|(pid, pt)| pid == &principal_id && pt == &principal_type);
1614 if !found {
1615 return Err(not_found("Application assignment not found."));
1616 }
1617 ok(json!({
1618 "ApplicationArn": arn,
1619 "PrincipalId": principal_id,
1620 "PrincipalType": principal_type,
1621 }))
1622 }
1623
1624 fn list_application_assignments(
1625 &self,
1626 req: &AwsRequest,
1627 ) -> Result<AwsResponse, AwsServiceError> {
1628 let b = parse(req)?;
1629 validate_common(&b)?;
1630 let arn = req_str(&b, "ApplicationArn")?.to_string();
1631 let guard = self.state.read();
1632 let rows: Vec<Value> = self
1633 .application(&guard, &req.account_id, &arn)
1634 .map(|app| {
1635 app.assignments
1636 .iter()
1637 .map(|(pid, pt)| {
1638 json!({ "ApplicationArn": arn, "PrincipalId": pid, "PrincipalType": pt })
1639 })
1640 .collect()
1641 })
1642 .unwrap_or_default();
1643 list_response("ApplicationAssignments", rows, &b)
1644 }
1645
1646 fn list_application_assignments_for_principal(
1647 &self,
1648 req: &AwsRequest,
1649 ) -> Result<AwsResponse, AwsServiceError> {
1650 let b = parse(req)?;
1651 validate_common(&b)?;
1652 req_str(&b, "InstanceArn")?;
1653 let principal_id = req_str(&b, "PrincipalId")?.to_string();
1654 let principal_type = req_str(&b, "PrincipalType")?.to_string();
1655 let guard = self.state.read();
1656 let rows: Vec<Value> = guard
1657 .get(&req.account_id)
1658 .map(|a| {
1659 a.applications
1660 .values()
1661 .flat_map(|app| {
1662 app.assignments
1663 .iter()
1664 .filter(|(pid, pt)| pid == &principal_id && pt == &principal_type)
1665 .map(|(pid, pt)| {
1666 json!({
1667 "ApplicationArn": app.arn,
1668 "PrincipalId": pid,
1669 "PrincipalType": pt,
1670 })
1671 })
1672 .collect::<Vec<_>>()
1673 })
1674 .collect()
1675 })
1676 .unwrap_or_default();
1677 list_response("ApplicationAssignments", rows, &b)
1678 }
1679
1680 fn get_application_assignment_configuration(
1681 &self,
1682 req: &AwsRequest,
1683 ) -> Result<AwsResponse, AwsServiceError> {
1684 let b = parse(req)?;
1685 validate_common(&b)?;
1686 let arn = req_str(&b, "ApplicationArn")?.to_string();
1687 let guard = self.state.read();
1688 let app = self
1689 .application(&guard, &req.account_id, &arn)
1690 .ok_or_else(|| not_found("Application not found."))?;
1691 ok(json!({ "AssignmentRequired": app.assignment_required.unwrap_or(true) }))
1693 }
1694
1695 fn put_application_assignment_configuration(
1696 &self,
1697 req: &AwsRequest,
1698 ) -> Result<AwsResponse, AwsServiceError> {
1699 let b = parse(req)?;
1700 validate_common(&b)?;
1701 let required = b
1702 .get("AssignmentRequired")
1703 .and_then(Value::as_bool)
1704 .ok_or_else(|| validation("AssignmentRequired must be specified."))?;
1705 self.with_application(req, &b, |app| {
1706 app.assignment_required = Some(required);
1707 ok(json!({}))
1708 })
1709 }
1710
1711 fn put_application_access_scope(
1714 &self,
1715 req: &AwsRequest,
1716 ) -> Result<AwsResponse, AwsServiceError> {
1717 let b = parse(req)?;
1718 validate_common(&b)?;
1719 let scope = req_str(&b, "Scope")?.to_string();
1720 let targets = b.get("AuthorizedTargets").cloned();
1721 self.with_application(req, &b, |app| {
1722 app.access_scopes
1723 .insert(scope, targets.unwrap_or(Value::Null));
1724 ok(json!({}))
1725 })
1726 }
1727
1728 fn get_application_access_scope(
1729 &self,
1730 req: &AwsRequest,
1731 ) -> Result<AwsResponse, AwsServiceError> {
1732 let b = parse(req)?;
1733 validate_common(&b)?;
1734 let arn = req_str(&b, "ApplicationArn")?.to_string();
1735 let scope = req_str(&b, "Scope")?.to_string();
1736 let guard = self.state.read();
1737 let app = self
1738 .application(&guard, &req.account_id, &arn)
1739 .ok_or_else(|| not_found("Application not found."))?;
1740 let targets = app
1741 .access_scopes
1742 .get(&scope)
1743 .ok_or_else(|| not_found("Access scope not found."))?;
1744 let mut m = serde_json::Map::new();
1745 m.insert("Scope".into(), json!(scope));
1746 if !targets.is_null() {
1747 m.insert("AuthorizedTargets".into(), targets.clone());
1748 }
1749 ok(Value::Object(m))
1750 }
1751
1752 fn delete_application_access_scope(
1753 &self,
1754 req: &AwsRequest,
1755 ) -> Result<AwsResponse, AwsServiceError> {
1756 let b = parse(req)?;
1757 validate_common(&b)?;
1758 let scope = req_str(&b, "Scope")?.to_string();
1759 self.with_application(req, &b, |app| {
1760 app.access_scopes.remove(&scope);
1761 ok(json!({}))
1762 })
1763 }
1764
1765 fn list_application_access_scopes(
1766 &self,
1767 req: &AwsRequest,
1768 ) -> Result<AwsResponse, AwsServiceError> {
1769 let b = parse(req)?;
1770 validate_common(&b)?;
1771 let arn = req_str(&b, "ApplicationArn")?.to_string();
1772 let guard = self.state.read();
1773 let rows: Vec<Value> = self
1774 .application(&guard, &req.account_id, &arn)
1775 .map(|app| {
1776 app.access_scopes
1777 .iter()
1778 .map(|(scope, targets)| {
1779 let mut m = serde_json::Map::new();
1780 m.insert("Scope".into(), json!(scope));
1781 if !targets.is_null() {
1782 m.insert("AuthorizedTargets".into(), targets.clone());
1783 }
1784 Value::Object(m)
1785 })
1786 .collect()
1787 })
1788 .unwrap_or_default();
1789 list_response("Scopes", rows, &b)
1790 }
1791
1792 fn put_application_authentication_method(
1795 &self,
1796 req: &AwsRequest,
1797 ) -> Result<AwsResponse, AwsServiceError> {
1798 let b = parse(req)?;
1799 validate_common(&b)?;
1800 let mtype = req_str(&b, "AuthenticationMethodType")?.to_string();
1801 let method = b
1802 .get("AuthenticationMethod")
1803 .cloned()
1804 .ok_or_else(|| validation("AuthenticationMethod must be specified."))?;
1805 self.with_application(req, &b, |app| {
1806 app.auth_methods.insert(mtype, method);
1807 ok(json!({}))
1808 })
1809 }
1810
1811 fn get_application_authentication_method(
1812 &self,
1813 req: &AwsRequest,
1814 ) -> Result<AwsResponse, AwsServiceError> {
1815 let b = parse(req)?;
1816 validate_common(&b)?;
1817 let arn = req_str(&b, "ApplicationArn")?.to_string();
1818 let mtype = req_str(&b, "AuthenticationMethodType")?.to_string();
1819 let guard = self.state.read();
1820 let app = self
1821 .application(&guard, &req.account_id, &arn)
1822 .ok_or_else(|| not_found("Application not found."))?;
1823 let method = app
1824 .auth_methods
1825 .get(&mtype)
1826 .ok_or_else(|| not_found("Authentication method not found."))?;
1827 ok(json!({ "AuthenticationMethod": method }))
1828 }
1829
1830 fn delete_application_authentication_method(
1831 &self,
1832 req: &AwsRequest,
1833 ) -> Result<AwsResponse, AwsServiceError> {
1834 let b = parse(req)?;
1835 validate_common(&b)?;
1836 let mtype = req_str(&b, "AuthenticationMethodType")?.to_string();
1837 self.with_application(req, &b, |app| {
1838 app.auth_methods.remove(&mtype);
1839 ok(json!({}))
1840 })
1841 }
1842
1843 fn list_application_authentication_methods(
1844 &self,
1845 req: &AwsRequest,
1846 ) -> Result<AwsResponse, AwsServiceError> {
1847 let b = parse(req)?;
1848 validate_common(&b)?;
1849 let arn = req_str(&b, "ApplicationArn")?.to_string();
1850 let guard = self.state.read();
1851 let rows: Vec<Value> = self
1852 .application(&guard, &req.account_id, &arn)
1853 .map(|app| {
1854 app.auth_methods
1855 .iter()
1856 .map(|(t, m)| json!({ "AuthenticationMethodType": t, "AuthenticationMethod": m }))
1857 .collect()
1858 })
1859 .unwrap_or_default();
1860 list_response("AuthenticationMethods", rows, &b)
1861 }
1862
1863 fn put_application_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1866 let b = parse(req)?;
1867 validate_common(&b)?;
1868 let gtype = req_str(&b, "GrantType")?.to_string();
1869 let grant = b
1870 .get("Grant")
1871 .cloned()
1872 .ok_or_else(|| validation("Grant must be specified."))?;
1873 self.with_application(req, &b, |app| {
1874 app.grants.insert(gtype, grant);
1875 ok(json!({}))
1876 })
1877 }
1878
1879 fn get_application_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1880 let b = parse(req)?;
1881 validate_common(&b)?;
1882 let arn = req_str(&b, "ApplicationArn")?.to_string();
1883 let gtype = req_str(&b, "GrantType")?.to_string();
1884 let guard = self.state.read();
1885 let app = self
1886 .application(&guard, &req.account_id, &arn)
1887 .ok_or_else(|| not_found("Application not found."))?;
1888 let grant = app
1889 .grants
1890 .get(>ype)
1891 .ok_or_else(|| not_found("Grant not found."))?;
1892 ok(json!({ "Grant": grant }))
1893 }
1894
1895 fn delete_application_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1896 let b = parse(req)?;
1897 validate_common(&b)?;
1898 let gtype = req_str(&b, "GrantType")?.to_string();
1899 self.with_application(req, &b, |app| {
1900 app.grants.remove(>ype);
1901 ok(json!({}))
1902 })
1903 }
1904
1905 fn list_application_grants(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1906 let b = parse(req)?;
1907 validate_common(&b)?;
1908 let arn = req_str(&b, "ApplicationArn")?.to_string();
1909 let guard = self.state.read();
1910 let rows: Vec<Value> = self
1911 .application(&guard, &req.account_id, &arn)
1912 .map(|app| {
1913 app.grants
1914 .iter()
1915 .map(|(t, g)| json!({ "GrantType": t, "Grant": g }))
1916 .collect()
1917 })
1918 .unwrap_or_default();
1919 list_response("Grants", rows, &b)
1920 }
1921
1922 fn get_application_session_configuration(
1925 &self,
1926 req: &AwsRequest,
1927 ) -> Result<AwsResponse, AwsServiceError> {
1928 let b = parse(req)?;
1929 validate_common(&b)?;
1930 let arn = req_str(&b, "ApplicationArn")?.to_string();
1931 let guard = self.state.read();
1932 let app = self
1933 .application(&guard, &req.account_id, &arn)
1934 .ok_or_else(|| not_found("Application not found."))?;
1935 let mut m = serde_json::Map::new();
1936 if let Some(status) = &app.session_status {
1937 m.insert(
1938 "UserBackgroundSessionApplicationStatus".into(),
1939 json!(status),
1940 );
1941 }
1942 ok(Value::Object(m))
1943 }
1944
1945 fn put_application_session_configuration(
1946 &self,
1947 req: &AwsRequest,
1948 ) -> Result<AwsResponse, AwsServiceError> {
1949 let b = parse(req)?;
1950 validate_common(&b)?;
1951 let status = b
1952 .get("UserBackgroundSessionApplicationStatus")
1953 .and_then(Value::as_str)
1954 .map(str::to_string);
1955 self.with_application(req, &b, |app| {
1956 app.session_status = status;
1957 ok(json!({}))
1958 })
1959 }
1960
1961 fn list_application_providers(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1964 let b = parse(req)?;
1965 validate_common(&b)?;
1966 let rows: Vec<Value> = APPLICATION_PROVIDERS
1967 .iter()
1968 .map(|(arn, proto, display)| {
1969 json!({
1970 "ApplicationProviderArn": arn,
1971 "FederationProtocol": proto,
1972 "DisplayData": { "DisplayName": display },
1973 })
1974 })
1975 .collect();
1976 list_response("ApplicationProviders", rows, &b)
1977 }
1978
1979 fn describe_application_provider(
1980 &self,
1981 req: &AwsRequest,
1982 ) -> Result<AwsResponse, AwsServiceError> {
1983 let b = parse(req)?;
1984 validate_common(&b)?;
1985 let arn = req_str(&b, "ApplicationProviderArn")?.to_string();
1986 let found = APPLICATION_PROVIDERS
1987 .iter()
1988 .find(|(a, _, _)| *a == arn)
1989 .ok_or_else(|| not_found("Application provider not found."))?;
1990 ok(json!({
1991 "ApplicationProviderArn": found.0,
1992 "FederationProtocol": found.1,
1993 "DisplayData": { "DisplayName": found.2 },
1994 }))
1995 }
1996
1997 fn create_trusted_token_issuer(
2000 &self,
2001 req: &AwsRequest,
2002 ) -> Result<AwsResponse, AwsServiceError> {
2003 let b = parse(req)?;
2004 validate_common(&b)?;
2005 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2006 let name = req_str(&b, "Name")?.to_string();
2007 check_len(&b, "Name", 1, 255)?;
2008 req_str(&b, "TrustedTokenIssuerType")?;
2009 let guard = self.state.read();
2012 let ssoins = guard
2013 .get(&req.account_id)
2014 .and_then(|a| a.instances.get(&instance_arn))
2015 .map(|i| i.ssoins.clone())
2016 .unwrap_or_else(|| format!("ssoins-{}", hex16()));
2017 drop(guard);
2018 let arn = format!(
2019 "arn:aws:sso::{}:trustedTokenIssuer/{ssoins}/tti-{}",
2020 req.account_id,
2021 Uuid::new_v4()
2022 );
2023 let tti = StoredTrustedTokenIssuer {
2024 arn: arn.clone(),
2025 instance_arn,
2026 name: Some(name),
2027 issuer_type: b
2028 .get("TrustedTokenIssuerType")
2029 .and_then(Value::as_str)
2030 .unwrap_or("OIDC_JWT")
2031 .to_string(),
2032 config: b.get("TrustedTokenIssuerConfiguration").cloned(),
2033 };
2034 {
2035 let mut guard = self.state.write();
2036 let acct = guard.get_or_create(&req.account_id);
2037 acct.trusted_token_issuers.insert(arn.clone(), tti);
2038 store_tags(acct, &arn, &b);
2039 }
2040 ok(json!({ "TrustedTokenIssuerArn": arn }))
2041 }
2042
2043 fn describe_trusted_token_issuer(
2044 &self,
2045 req: &AwsRequest,
2046 ) -> Result<AwsResponse, AwsServiceError> {
2047 let b = parse(req)?;
2048 validate_common(&b)?;
2049 let arn = req_str(&b, "TrustedTokenIssuerArn")?.to_string();
2050 let guard = self.state.read();
2051 let tti = guard
2052 .get(&req.account_id)
2053 .and_then(|a| a.trusted_token_issuers.get(&arn))
2054 .ok_or_else(|| not_found("Trusted token issuer not found."))?;
2055 ok(build_tti_describe(tti))
2056 }
2057
2058 fn update_trusted_token_issuer(
2059 &self,
2060 req: &AwsRequest,
2061 ) -> Result<AwsResponse, AwsServiceError> {
2062 let b = parse(req)?;
2063 validate_common(&b)?;
2064 check_len(&b, "Name", 1, 255)?;
2065 let arn = req_str(&b, "TrustedTokenIssuerArn")?.to_string();
2066 let mut guard = self.state.write();
2067 let tti = guard
2068 .get_mut(&req.account_id)
2069 .and_then(|a| a.trusted_token_issuers.get_mut(&arn))
2070 .ok_or_else(|| not_found("Trusted token issuer not found."))?;
2071 if let Some(n) = b.get("Name").and_then(Value::as_str) {
2072 tti.name = Some(n.to_string());
2073 }
2074 if let Some(c) = b.get("TrustedTokenIssuerConfiguration") {
2075 match &mut tti.config {
2079 Some(existing) => deep_merge(existing, c),
2080 None => tti.config = Some(c.clone()),
2081 }
2082 }
2083 ok(json!({}))
2084 }
2085
2086 fn delete_trusted_token_issuer(
2087 &self,
2088 req: &AwsRequest,
2089 ) -> Result<AwsResponse, AwsServiceError> {
2090 let b = parse(req)?;
2091 validate_common(&b)?;
2092 let arn = req_str(&b, "TrustedTokenIssuerArn")?.to_string();
2093 if let Some(a) = self.state.write().get_mut(&req.account_id) {
2094 a.trusted_token_issuers.remove(&arn);
2095 }
2096 ok(json!({}))
2097 }
2098
2099 fn list_trusted_token_issuers(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2100 let b = parse(req)?;
2101 validate_common(&b)?;
2102 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2103 let guard = self.state.read();
2104 let rows: Vec<Value> = guard
2105 .get(&req.account_id)
2106 .map(|a| {
2107 a.trusted_token_issuers
2108 .values()
2109 .filter(|t| t.instance_arn == instance_arn)
2110 .map(build_tti_metadata)
2111 .collect()
2112 })
2113 .unwrap_or_default();
2114 list_response("TrustedTokenIssuers", rows, &b)
2115 }
2116
2117 fn add_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2120 let b = parse(req)?;
2121 validate_common(&b)?;
2122 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2123 let region_name = req_str(&b, "RegionName")?.to_string();
2124 let mut guard = self.state.write();
2125 let acct = guard.get_or_create(&req.account_id);
2126 let ssoins = instance_arn
2129 .rsplit('/')
2130 .next()
2131 .unwrap_or("ssoins-unknown")
2132 .to_string();
2133 let inst = acct
2134 .instances
2135 .entry(instance_arn.clone())
2136 .or_insert_with(|| StoredInstance {
2137 arn: instance_arn.clone(),
2138 ssoins,
2139 identity_store_id: format!("d-{}", hex10()),
2140 owner_account_id: req.account_id.clone(),
2141 name: None,
2142 status: "ACTIVE".into(),
2143 created_date: now(),
2144 regions: Vec::new(),
2145 primary_region: Some(req.region.clone()),
2146 attr_config: None,
2147 attr_config_status: None,
2148 });
2149 if !inst.regions.iter().any(|r| r.region_name == region_name) {
2150 let is_primary = inst.regions.is_empty();
2151 if is_primary {
2152 inst.primary_region = Some(region_name.clone());
2153 }
2154 inst.regions.push(StoredRegion {
2155 region_name,
2156 status: "ACTIVE".into(),
2157 added_date: now(),
2158 is_primary,
2159 });
2160 }
2161 ok(json!({ "Status": "ACTIVE" }))
2162 }
2163
2164 fn remove_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2165 let b = parse(req)?;
2166 validate_common(&b)?;
2167 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2168 let region_name = req_str(&b, "RegionName")?.to_string();
2169 if let Some(inst) = self
2170 .state
2171 .write()
2172 .get_mut(&req.account_id)
2173 .and_then(|a| a.instances.get_mut(&instance_arn))
2174 {
2175 inst.regions.retain(|r| r.region_name != region_name);
2176 }
2177 ok(json!({ "Status": "REMOVING" }))
2178 }
2179
2180 fn describe_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2181 let b = parse(req)?;
2182 validate_common(&b)?;
2183 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2184 let region_name = req_str(&b, "RegionName")?.to_string();
2185 let guard = self.state.read();
2186 let region = guard
2187 .get(&req.account_id)
2188 .and_then(|a| a.instances.get(&instance_arn))
2189 .and_then(|i| i.regions.iter().find(|r| r.region_name == region_name))
2190 .ok_or_else(|| not_found("Region not found."))?;
2191 ok(build_region(region))
2192 }
2193
2194 fn list_regions(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2195 let b = parse(req)?;
2196 validate_common(&b)?;
2197 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2198 let guard = self.state.read();
2199 let rows: Vec<Value> = guard
2200 .get(&req.account_id)
2201 .and_then(|a| a.instances.get(&instance_arn))
2202 .map(|i| i.regions.iter().map(build_region).collect())
2203 .unwrap_or_default();
2204 list_response("Regions", rows, &b)
2205 }
2206
2207 fn create_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2210 let b = parse(req)?;
2211 validate_common(&b)?;
2212 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2213 let config = b
2214 .get("InstanceAccessControlAttributeConfiguration")
2215 .cloned()
2216 .ok_or_else(|| {
2217 validation("InstanceAccessControlAttributeConfiguration must be specified.")
2218 })?;
2219 let mut guard = self.state.write();
2220 let inst = guard
2221 .get_mut(&req.account_id)
2222 .and_then(|a| a.instances.get_mut(&instance_arn))
2223 .ok_or_else(|| not_found("Instance not found."))?;
2224 inst.attr_config = Some(config);
2225 inst.attr_config_status = Some("ENABLED".into());
2226 ok(json!({}))
2227 }
2228
2229 fn describe_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2230 let b = parse(req)?;
2231 validate_common(&b)?;
2232 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2233 let guard = self.state.read();
2234 let inst = guard
2235 .get(&req.account_id)
2236 .and_then(|a| a.instances.get(&instance_arn))
2237 .ok_or_else(|| not_found("Instance not found."))?;
2238 let config = inst
2239 .attr_config
2240 .clone()
2241 .ok_or_else(|| not_found("Access control attribute configuration not found."))?;
2242 ok(json!({
2243 "Status": inst.attr_config_status.clone().unwrap_or_else(|| "ENABLED".into()),
2244 "InstanceAccessControlAttributeConfiguration": config,
2245 }))
2246 }
2247
2248 fn update_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2249 let b = parse(req)?;
2250 validate_common(&b)?;
2251 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2252 let config = b
2253 .get("InstanceAccessControlAttributeConfiguration")
2254 .cloned()
2255 .ok_or_else(|| {
2256 validation("InstanceAccessControlAttributeConfiguration must be specified.")
2257 })?;
2258 let mut guard = self.state.write();
2259 let inst = guard
2260 .get_mut(&req.account_id)
2261 .and_then(|a| a.instances.get_mut(&instance_arn))
2262 .ok_or_else(|| not_found("Instance not found."))?;
2263 inst.attr_config = Some(config);
2264 inst.attr_config_status = Some("ENABLED".into());
2265 ok(json!({}))
2266 }
2267
2268 fn delete_attr_config(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2269 let b = parse(req)?;
2270 validate_common(&b)?;
2271 let instance_arn = req_str(&b, "InstanceArn")?.to_string();
2272 if let Some(inst) = self
2273 .state
2274 .write()
2275 .get_mut(&req.account_id)
2276 .and_then(|a| a.instances.get_mut(&instance_arn))
2277 {
2278 inst.attr_config = None;
2279 inst.attr_config_status = None;
2280 }
2281 ok(json!({}))
2282 }
2283
2284 fn tag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2287 let b = parse(req)?;
2288 validate_common(&b)?;
2289 let resource_arn = req_str(&b, "ResourceArn")?.to_string();
2290 let tags = b
2291 .get("Tags")
2292 .and_then(Value::as_array)
2293 .ok_or_else(|| validation("Tags must be specified."))?;
2294 let mut guard = self.state.write();
2295 let entry = guard
2296 .get_or_create(&req.account_id)
2297 .tags
2298 .entry(resource_arn)
2299 .or_default();
2300 for tag in tags {
2301 if let (Some(k), Some(v)) = (
2302 tag.get("Key").and_then(Value::as_str),
2303 tag.get("Value").and_then(Value::as_str),
2304 ) {
2305 entry.insert(k.to_string(), v.to_string());
2306 }
2307 }
2308 ok(json!({}))
2309 }
2310
2311 fn untag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2312 let b = parse(req)?;
2313 validate_common(&b)?;
2314 let resource_arn = req_str(&b, "ResourceArn")?.to_string();
2315 let keys = b
2316 .get("TagKeys")
2317 .and_then(Value::as_array)
2318 .ok_or_else(|| validation("TagKeys must be specified."))?;
2319 if let Some(entry) = self
2320 .state
2321 .write()
2322 .get_mut(&req.account_id)
2323 .and_then(|a| a.tags.get_mut(&resource_arn))
2324 {
2325 for k in keys {
2326 if let Some(k) = k.as_str() {
2327 entry.remove(k);
2328 }
2329 }
2330 }
2331 ok(json!({}))
2332 }
2333
2334 fn list_tags_for_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2335 let b = parse(req)?;
2336 validate_common(&b)?;
2337 let resource_arn = req_str(&b, "ResourceArn")?.to_string();
2338 let guard = self.state.read();
2339 let rows: Vec<Value> = guard
2340 .get(&req.account_id)
2341 .and_then(|a| a.tags.get(&resource_arn))
2342 .map(|m| {
2343 m.iter()
2344 .map(|(k, v)| json!({ "Key": k, "Value": v }))
2345 .collect()
2346 })
2347 .unwrap_or_default();
2348 list_response("Tags", rows, &b)
2349 }
2350}
2351
2352#[cfg(test)]
2353mod tests;