awsim-iam 0.5.0

AWS IAM emulator for AWSim
Documentation
use awsim_core::{AwsError, RequestContext};
use serde_json::{Value, json};

use crate::{
    error::{entity_already_exists, no_such_entity},
    ids::{new_server_certificate_id, normalize_path, now_iso8601},
    state::{IamState, ServerCertificate},
};

use super::super::operations::tags::{parse_tag_keys, parse_tags, tags_to_value};
use super::{opt_str, require_str};

fn cert_metadata_to_value(c: &ServerCertificate) -> Value {
    let mut v = json!({
        "ServerCertificateName": c.server_certificate_name,
        "ServerCertificateId": c.server_certificate_id,
        "Arn": c.arn,
        "Path": c.path,
        "UploadDate": c.upload_date,
    });
    if let Some(exp) = &c.expiration {
        v["Expiration"] = Value::String(exp.clone());
    }
    v
}

fn cert_to_value(c: &ServerCertificate) -> Value {
    json!({
        "ServerCertificateMetadata": cert_metadata_to_value(c),
        "CertificateBody": c.certificate_body,
        "CertificateChain": c.certificate_chain.clone().unwrap_or_default(),
    })
}

pub fn upload_server_certificate(
    state: &IamState,
    input: &Value,
    ctx: &RequestContext,
) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;
    let certificate_body = require_str(input, "CertificateBody")?;
    let _private_key = require_str(input, "PrivateKey")?;
    let certificate_chain = opt_str(input, "CertificateChain").map(|s| s.to_string());
    let path = normalize_path(opt_str(input, "Path"));

    if state.server_certificates.contains_key(name) {
        return Err(entity_already_exists("ServerCertificate", name));
    }

    let cert_id = new_server_certificate_id();
    let arn = format!(
        "arn:aws:iam::{}:server-certificate{}{}",
        ctx.account_id, path, name
    );

    let cert = ServerCertificate {
        server_certificate_name: name.to_string(),
        server_certificate_id: cert_id,
        arn,
        path,
        certificate_body: certificate_body.to_string(),
        certificate_chain,
        upload_date: now_iso8601(),
        expiration: None,
        tags: std::collections::HashMap::new(),
    };

    let result = cert_metadata_to_value(&cert);
    state.server_certificates.insert(name.to_string(), cert);

    Ok(json!({ "ServerCertificateMetadata": result }))
}

pub fn get_server_certificate(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;
    let cert = state
        .server_certificates
        .get(name)
        .ok_or_else(|| no_such_entity("ServerCertificate", name))?;
    Ok(json!({ "ServerCertificate": cert_to_value(&cert) }))
}

pub fn list_server_certificates(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let path_prefix = opt_str(input, "PathPrefix").unwrap_or("/");
    let list: Vec<Value> = state
        .server_certificates
        .iter()
        .filter(|c| c.path.starts_with(path_prefix))
        .map(|c| cert_metadata_to_value(&c))
        .collect();

    Ok(json!({
        "ServerCertificateMetadataList": { "member": list },
        "IsTruncated": false,
    }))
}

pub fn delete_server_certificate(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;

    if state.server_certificates.remove(name).is_none() {
        return Err(no_such_entity("ServerCertificate", name));
    }

    Ok(json!({}))
}

pub fn tag_server_certificate(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;
    let new_tags = parse_tags(input)?;

    let mut cert = state
        .server_certificates
        .get_mut(name)
        .ok_or_else(|| no_such_entity("ServerCertificate", name))?;

    for (k, v) in new_tags {
        cert.tags.insert(k, v);
    }

    Ok(json!({}))
}

pub fn untag_server_certificate(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;
    let keys = parse_tag_keys(input)?;

    let mut cert = state
        .server_certificates
        .get_mut(name)
        .ok_or_else(|| no_such_entity("ServerCertificate", name))?;

    for k in &keys {
        cert.tags.remove(k);
    }

    Ok(json!({}))
}

pub fn update_server_certificate(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;
    let new_name = opt_str(input, "NewServerCertificateName");
    let new_path = opt_str(input, "NewPath");

    if !state.server_certificates.contains_key(name) {
        return Err(no_such_entity("ServerCertificate", name));
    }
    if let Some(nn) = new_name
        && nn != name
        && state.server_certificates.contains_key(nn)
    {
        return Err(entity_already_exists("ServerCertificate", nn));
    }
    if new_name.is_none() && new_path.is_none() {
        return Ok(json!({}));
    }
    let (_, mut cert) = state.server_certificates.remove(name).unwrap();
    if let Some(np) = new_path {
        cert.path = normalize_path(Some(np));
    }
    let final_name = if let Some(nn) = new_name {
        cert.server_certificate_name = nn.to_string();
        nn.to_string()
    } else {
        name.to_string()
    };
    state.server_certificates.insert(final_name, cert);
    Ok(json!({}))
}

pub fn list_server_certificate_tags(state: &IamState, input: &Value) -> Result<Value, AwsError> {
    let name = require_str(input, "ServerCertificateName")?;
    let cert = state
        .server_certificates
        .get(name)
        .ok_or_else(|| no_such_entity("ServerCertificate", name))?;

    Ok(json!({
        "Tags": tags_to_value(&cert.tags),
        "IsTruncated": false,
    }))
}