use std::collections::HashMap;
use awsim_core::{AwsError, RequestContext};
use serde_json::{Value, json};
use tracing::info;
use crate::error;
use crate::state::{Secret, SecretVersion, SecretsState};
use crate::util::{new_version_id, now_epoch_f64, random_password, random_suffix};
fn resolve_name(state: &SecretsState, secret_id: &str) -> Result<String, AwsError> {
if state.secrets.contains_key(secret_id) {
return Ok(secret_id.to_string());
}
if secret_id.starts_with("arn:aws:secretsmanager:") {
for entry in state.secrets.iter() {
if entry.value().arn == secret_id {
return Ok(entry.key().clone());
}
}
return Err(error::resource_not_found(secret_id));
}
Err(error::resource_not_found(secret_id))
}
fn build_arn(region: &str, account_id: &str, name: &str) -> String {
let suffix = random_suffix(6);
format!("arn:aws:secretsmanager:{region}:{account_id}:secret:{name}-{suffix}")
}
fn secret_metadata(secret: &Secret) -> Value {
let versions_to_stages: serde_json::Map<String, Value> = secret
.versions
.iter()
.map(|(vid, v)| {
let stages: Vec<Value> = v.stages.iter().map(|s| json!(s)).collect();
(vid.clone(), json!(stages))
})
.collect();
let mut meta = json!({
"ARN": secret.arn,
"Name": secret.name,
"Description": secret.description,
"CreatedDate": secret.created_date,
"LastChangedDate": secret.last_changed_date,
"VersionIdsToStages": versions_to_stages,
"RotationEnabled": secret.rotation_enabled,
});
if let Some(ref arn) = secret.rotation_lambda_arn {
meta["RotationLambdaARN"] = json!(arn);
}
if let Some(days) = secret.rotation_automatically_after_days {
meta["RotationRules"] = json!({ "AutomaticallyAfterDays": days });
}
if !secret.tags.is_empty() {
let tags: Vec<Value> = secret
.tags
.iter()
.map(|(k, v)| json!({ "Key": k, "Value": v }))
.collect();
meta["Tags"] = json!(tags);
}
if let Some(ref dd) = secret.deleted_date {
meta["DeletedDate"] = json!(dd);
}
meta
}
pub fn create_secret(
state: &SecretsState,
input: &Value,
ctx: &RequestContext,
) -> Result<Value, AwsError> {
let name = input["Name"]
.as_str()
.ok_or_else(|| error::missing_parameter("Name"))?;
if state.secrets.contains_key(name) {
return Err(error::resource_exists(name));
}
let description = input["Description"].as_str().unwrap_or("").to_string();
let secret_string = input["SecretString"].as_str().map(|s| s.to_string());
let secret_binary = input["SecretBinary"].as_str().map(|s| s.to_string());
if secret_string.is_none() && secret_binary.is_none() {
return Err(error::invalid_parameter(
"Either SecretString or SecretBinary must be provided",
));
}
let mut tags = HashMap::new();
if let Some(tag_list) = input["Tags"].as_array() {
for tag in tag_list {
if let (Some(k), Some(v)) = (tag["Key"].as_str(), tag["Value"].as_str()) {
tags.insert(k.to_string(), v.to_string());
}
}
}
let arn = build_arn(&ctx.region, &ctx.account_id, name);
let now = now_epoch_f64();
let version_id = new_version_id();
let version = SecretVersion {
version_id: version_id.clone(),
secret_string,
secret_binary,
stages: vec!["AWSCURRENT".to_string()],
created_date: now,
};
let mut versions = HashMap::new();
versions.insert(version_id.clone(), version);
let secret = Secret {
arn: arn.clone(),
name: name.to_string(),
description,
versions,
current_version_id: version_id.clone(),
tags,
created_date: now,
last_changed_date: now,
deleted_date: None,
rotation_enabled: false,
rotation_lambda_arn: None,
rotation_automatically_after_days: None,
};
info!(name = %name, arn = %arn, "Created secret");
state.secrets.insert(name.to_string(), secret);
Ok(json!({
"ARN": arn,
"Name": name,
"VersionId": version_id,
}))
}
pub fn get_secret_value(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if secret.deleted_date.is_some() {
return Err(error::invalid_request("Secret is marked for deletion"));
}
let version_stage = input["VersionStage"].as_str().unwrap_or("AWSCURRENT");
let version_id = if let Some(vid) = input["VersionId"].as_str() {
if !secret.versions.contains_key(vid) {
return Err(error::resource_not_found(vid));
}
vid.to_string()
} else {
secret
.versions
.iter()
.find(|(_, v)| v.stages.contains(&version_stage.to_string()))
.map(|(id, _)| id.clone())
.ok_or_else(|| error::resource_not_found(&format!("stage {version_stage}")))?
};
let version = secret
.versions
.get(&version_id)
.ok_or_else(|| error::resource_not_found(&version_id))?;
let mut response = json!({
"ARN": secret.arn,
"Name": secret.name,
"VersionId": version.version_id,
"VersionStages": version.stages,
"CreatedDate": version.created_date,
});
if let Some(ref ss) = version.secret_string {
response["SecretString"] = json!(ss);
}
if let Some(ref sb) = version.secret_binary {
response["SecretBinary"] = json!(sb);
}
Ok(response)
}
pub fn put_secret_value(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if secret.deleted_date.is_some() {
return Err(error::invalid_request("Secret is marked for deletion"));
}
let secret_string = input["SecretString"].as_str().map(|s| s.to_string());
let secret_binary = input["SecretBinary"].as_str().map(|s| s.to_string());
if secret_string.is_none() && secret_binary.is_none() {
return Err(error::invalid_parameter(
"Either SecretString or SecretBinary must be provided",
));
}
let now = now_epoch_f64();
let new_version_id_str = new_version_id();
let requested_stages: Vec<String> = if let Some(stages) = input["VersionStages"].as_array() {
stages
.iter()
.filter_map(|s| s.as_str().map(|s| s.to_string()))
.collect()
} else {
vec!["AWSCURRENT".to_string()]
};
if requested_stages.contains(&"AWSCURRENT".to_string()) {
let old_current = secret.current_version_id.clone();
if let Some(old_ver) = secret.versions.get_mut(&old_current) {
old_ver.stages.retain(|s| s != "AWSCURRENT");
if !old_ver.stages.contains(&"AWSPREVIOUS".to_string()) {
old_ver.stages.push("AWSPREVIOUS".to_string());
}
}
secret.current_version_id = new_version_id_str.clone();
}
let new_version = SecretVersion {
version_id: new_version_id_str.clone(),
secret_string,
secret_binary,
stages: requested_stages,
created_date: now,
};
secret
.versions
.insert(new_version_id_str.clone(), new_version);
secret.last_changed_date = now;
let arn = secret.arn.clone();
let sname = secret.name.clone();
drop(secret);
Ok(json!({
"ARN": arn,
"Name": sname,
"VersionId": new_version_id_str,
}))
}
pub fn describe_secret(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
Ok(secret_metadata(&secret))
}
pub fn list_secrets(
state: &SecretsState,
_input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let list: Vec<Value> = state
.secrets
.iter()
.map(|entry| secret_metadata(entry.value()))
.collect();
Ok(json!({ "SecretList": list }))
}
pub fn update_secret(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if secret.deleted_date.is_some() {
return Err(error::invalid_request("Secret is marked for deletion"));
}
if let Some(desc) = input["Description"].as_str() {
secret.description = desc.to_string();
}
let has_new_value =
input["SecretString"].as_str().is_some() || input["SecretBinary"].as_str().is_some();
let now = now_epoch_f64();
if has_new_value {
let secret_string = input["SecretString"].as_str().map(|s| s.to_string());
let secret_binary = input["SecretBinary"].as_str().map(|s| s.to_string());
let new_vid = new_version_id();
let old_current = secret.current_version_id.clone();
if let Some(old_ver) = secret.versions.get_mut(&old_current) {
old_ver.stages.retain(|s| s != "AWSCURRENT");
if !old_ver.stages.contains(&"AWSPREVIOUS".to_string()) {
old_ver.stages.push("AWSPREVIOUS".to_string());
}
}
let new_version = SecretVersion {
version_id: new_vid.clone(),
secret_string,
secret_binary,
stages: vec!["AWSCURRENT".to_string()],
created_date: now,
};
secret.versions.insert(new_vid.clone(), new_version);
secret.current_version_id = new_vid;
}
secret.last_changed_date = now;
let arn = secret.arn.clone();
let sname = secret.name.clone();
let vid = secret.current_version_id.clone();
drop(secret);
Ok(json!({
"ARN": arn,
"Name": sname,
"VersionId": vid,
}))
}
pub fn delete_secret(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if secret.deleted_date.is_some() {
return Err(error::invalid_request(
"Secret is already scheduled for deletion",
));
}
let force = input["ForceDeleteWithoutRecovery"]
.as_bool()
.unwrap_or(false);
let arn = secret.arn.clone();
let sname = secret.name.clone();
if force {
drop(secret);
state.secrets.remove(&name);
return Ok(json!({
"ARN": arn,
"Name": sname,
"DeletionDate": now_epoch_f64(),
}));
}
let recovery_days = input["RecoveryWindowInDays"].as_u64().unwrap_or(30);
if !(7..=30).contains(&recovery_days) {
return Err(error::invalid_parameter(
"RecoveryWindowInDays must be between 7 and 30",
));
}
use std::time::{SystemTime, UNIX_EPOCH};
let deletion_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs_f64()
+ (recovery_days * 86400) as f64;
secret.deleted_date = Some(deletion_epoch);
drop(secret);
info!(name = %name, "Secret scheduled for deletion");
Ok(json!({
"ARN": arn,
"Name": sname,
"DeletionDate": deletion_epoch,
}))
}
pub fn restore_secret(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if secret.deleted_date.is_none() {
return Err(error::invalid_request(
"Secret is not scheduled for deletion",
));
}
secret.deleted_date = None;
let arn = secret.arn.clone();
let sname = secret.name.clone();
drop(secret);
Ok(json!({ "ARN": arn, "Name": sname }))
}
pub fn tag_resource(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if let Some(tag_list) = input["Tags"].as_array() {
for tag in tag_list {
if let (Some(k), Some(v)) = (tag["Key"].as_str(), tag["Value"].as_str()) {
secret.tags.insert(k.to_string(), v.to_string());
}
}
}
Ok(json!({}))
}
pub fn untag_resource(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if let Some(key_list) = input["TagKeys"].as_array() {
for key in key_list {
if let Some(k) = key.as_str() {
secret.tags.remove(k);
}
}
}
Ok(json!({}))
}
pub fn rotate_secret(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
if secret.deleted_date.is_some() {
return Err(error::invalid_request("Secret is marked for deletion"));
}
if let Some(lambda_arn) = input["RotationLambdaARN"].as_str() {
secret.rotation_lambda_arn = Some(lambda_arn.to_string());
}
if let Some(rules) = input["RotationRules"].as_object()
&& let Some(days) = rules.get("AutomaticallyAfterDays").and_then(|v| v.as_u64())
{
secret.rotation_automatically_after_days = Some(days);
}
secret.rotation_enabled = true;
let now = now_epoch_f64();
let pending_vid = new_version_id();
let current_value = secret
.versions
.get(&secret.current_version_id)
.map(|v| (v.secret_string.clone(), v.secret_binary.clone()));
let (secret_string, secret_binary) = current_value.unwrap_or((None, None));
let old_current_id = secret.current_version_id.clone();
if let Some(old_ver) = secret.versions.get_mut(&old_current_id) {
old_ver.stages.retain(|s| s != "AWSCURRENT");
if !old_ver.stages.contains(&"AWSPREVIOUS".to_string()) {
old_ver.stages.push("AWSPREVIOUS".to_string());
}
}
let new_version = SecretVersion {
version_id: pending_vid.clone(),
secret_string,
secret_binary,
stages: vec!["AWSCURRENT".to_string()],
created_date: now,
};
secret.versions.insert(pending_vid.clone(), new_version);
secret.current_version_id = pending_vid.clone();
secret.last_changed_date = now;
let arn = secret.arn.clone();
let sname = secret.name.clone();
drop(secret);
info!(name = %name, "RotateSecret (stub)");
Ok(json!({
"ARN": arn,
"Name": sname,
"VersionId": pending_vid,
}))
}
pub fn cancel_rotate_secret(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
secret.rotation_enabled = false;
secret.rotation_lambda_arn = None;
let arn = secret.arn.clone();
let sname = secret.name.clone();
let vid = secret.current_version_id.clone();
drop(secret);
Ok(json!({
"ARN": arn,
"Name": sname,
"VersionId": vid,
}))
}
pub fn validate_resource_policy(
_state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
if input["ResourcePolicy"].as_str().is_none() {
return Err(error::missing_parameter("ResourcePolicy"));
}
Ok(json!({
"ValidationErrors": [],
}))
}
pub fn get_random_password(
_state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let length = input["PasswordLength"].as_u64().unwrap_or(32) as usize;
if !(1..=4096).contains(&length) {
return Err(error::invalid_parameter(
"PasswordLength must be between 1 and 4096",
));
}
let exclude_uppercase = input["ExcludeUppercase"].as_bool().unwrap_or(false);
let exclude_lowercase = input["ExcludeLowercase"].as_bool().unwrap_or(false);
let exclude_numbers = input["ExcludeNumbers"].as_bool().unwrap_or(false);
let exclude_punctuation = input["ExcludePunctuation"].as_bool().unwrap_or(false);
let password = random_password(
length,
exclude_uppercase,
exclude_lowercase,
exclude_numbers,
exclude_punctuation,
);
Ok(json!({ "RandomPassword": password }))
}
pub fn replicate_secret_to_regions(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
drop(secret);
Ok(json!({
"ARN": arn,
"ReplicationStatus": [],
}))
}
pub fn remove_regions_from_replication(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
drop(secret);
Ok(json!({
"ARN": arn,
"ReplicationStatus": [],
}))
}
pub fn stop_replication_to_replica(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
drop(secret);
Ok(json!({ "ARN": arn }))
}
pub fn list_secret_version_ids(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let include_deprecated = input["IncludeDeprecated"].as_bool().unwrap_or(false);
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let versions: Vec<Value> = secret
.versions
.iter()
.filter(|(_, v)| include_deprecated || !v.stages.is_empty())
.map(|(vid, v)| {
let stages: Vec<Value> = v.stages.iter().map(|s| json!(s)).collect();
json!({
"VersionId": vid,
"VersionStages": stages,
"CreatedDate": v.created_date,
})
})
.collect();
Ok(json!({
"ARN": secret.arn,
"Name": secret.name,
"Versions": versions,
"Truncated": false,
}))
}
pub fn batch_get_secret_value(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id_list = input["SecretIdList"]
.as_array()
.cloned()
.unwrap_or_default();
let mut secret_values: Vec<Value> = Vec::new();
let mut errors: Vec<Value> = Vec::new();
for id_val in &secret_id_list {
let secret_id = match id_val.as_str() {
Some(s) => s,
None => continue,
};
match resolve_name(state, secret_id) {
Ok(name) => {
let secret = match state.secrets.get(&name) {
Some(s) => s,
None => {
errors.push(json!({
"SecretId": secret_id,
"ErrorCode": "ResourceNotFoundException",
"Message": format!("Secrets Manager can't find the specified secret: {secret_id}"),
}));
continue;
}
};
if secret.deleted_date.is_some() {
errors.push(json!({
"SecretId": secret_id,
"ErrorCode": "InvalidRequestException",
"Message": "Secret is marked for deletion",
}));
continue;
}
let version = match secret.versions.get(&secret.current_version_id) {
Some(v) => v,
None => {
errors.push(json!({
"SecretId": secret_id,
"ErrorCode": "ResourceNotFoundException",
"Message": "No current version found",
}));
continue;
}
};
let mut entry = json!({
"ARN": secret.arn,
"Name": secret.name,
"VersionId": version.version_id,
"VersionStages": version.stages,
"CreatedDate": version.created_date,
});
if let Some(ref ss) = version.secret_string {
entry["SecretString"] = json!(ss);
}
if let Some(ref sb) = version.secret_binary {
entry["SecretBinary"] = json!(sb);
}
secret_values.push(entry);
}
Err(_) => {
errors.push(json!({
"SecretId": secret_id,
"ErrorCode": "ResourceNotFoundException",
"Message": format!("Secrets Manager can't find the specified secret: {secret_id}"),
}));
}
}
}
Ok(json!({
"SecretValues": secret_values,
"Errors": errors,
}))
}
pub fn update_secret_version_stage(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let version_stage = input["VersionStage"]
.as_str()
.ok_or_else(|| error::missing_parameter("VersionStage"))?;
let remove_from = input["RemoveFromVersionId"].as_str();
let move_to = input["MoveToVersionId"].as_str();
let name = resolve_name(state, secret_id)?;
let mut secret = state
.secrets
.get_mut(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
let secret_name = secret.name.clone();
if let Some(remove_id) = remove_from
&& let Some(v) = secret.versions.get_mut(remove_id)
{
v.stages.retain(|s| s != version_stage);
}
if let Some(move_id) = move_to {
if !secret.versions.contains_key(move_id) {
return Err(error::resource_not_found(move_id));
}
for (vid, v) in secret.versions.iter_mut() {
if vid != move_id {
v.stages.retain(|s| s != version_stage);
}
}
if let Some(v) = secret.versions.get_mut(move_id)
&& !v.stages.contains(&version_stage.to_string())
{
v.stages.push(version_stage.to_string());
}
if version_stage == "AWSCURRENT" {
secret.current_version_id = move_id.to_string();
}
}
secret.last_changed_date = now_epoch_f64();
Ok(json!({
"ARN": arn,
"Name": secret_name,
}))
}
pub fn put_resource_policy(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let policy = input["ResourcePolicy"]
.as_str()
.ok_or_else(|| error::missing_parameter("ResourcePolicy"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
let secret_name = secret.name.clone();
drop(secret);
state.resource_policies.insert(name, policy.to_string());
Ok(json!({
"ARN": arn,
"Name": secret_name,
}))
}
pub fn get_resource_policy(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
let secret_name = secret.name.clone();
drop(secret);
let policy = state
.resource_policies
.get(&name)
.map(|e| e.value().clone());
let mut response = json!({
"ARN": arn,
"Name": secret_name,
});
if let Some(p) = policy {
response["ResourcePolicy"] = json!(p);
}
Ok(response)
}
pub fn delete_resource_policy(
state: &SecretsState,
input: &Value,
_ctx: &RequestContext,
) -> Result<Value, AwsError> {
let secret_id = input["SecretId"]
.as_str()
.ok_or_else(|| error::missing_parameter("SecretId"))?;
let name = resolve_name(state, secret_id)?;
let secret = state
.secrets
.get(&name)
.ok_or_else(|| error::resource_not_found(secret_id))?;
let arn = secret.arn.clone();
let secret_name = secret.name.clone();
drop(secret);
state.resource_policies.remove(&name);
Ok(json!({
"ARN": arn,
"Name": secret_name,
}))
}