use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::client::HttpClient;
use crate::error::Result;
use crate::types::{QueryMeta, QueryOptions, WriteMeta, WriteOptions};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ACLTokenType {
#[serde(rename = "client")]
Client,
#[serde(rename = "management")]
Management,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLPolicy {
#[serde(rename = "ID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub name: String,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub description: String,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub rules: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub datacenters: Vec<String>,
#[serde(default)]
pub hash: String,
#[serde(default)]
pub create_index: u64,
#[serde(default)]
pub modify_index: u64,
}
impl ACLPolicy {
pub fn new(name: &str) -> Self {
Self {
id: None,
name: name.to_string(),
description: String::new(),
rules: String::new(),
datacenters: Vec::new(),
hash: String::new(),
create_index: 0,
modify_index: 0,
}
}
pub fn with_description(mut self, description: &str) -> Self {
self.description = description.to_string();
self
}
pub fn with_rules(mut self, rules: &str) -> Self {
self.rules = rules.to_string();
self
}
pub fn with_datacenters(mut self, datacenters: Vec<String>) -> Self {
self.datacenters = datacenters;
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLTokenPolicyLink {
#[serde(rename = "ID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
impl ACLTokenPolicyLink {
pub fn by_id(id: &str) -> Self {
Self {
id: Some(id.to_string()),
name: None,
}
}
pub fn by_name(name: &str) -> Self {
Self {
id: None,
name: Some(name.to_string()),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLTokenRoleLink {
#[serde(rename = "ID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLToken {
#[serde(rename = "AccessorID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub accessor_id: Option<String>,
#[serde(rename = "SecretID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub secret_id: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub description: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub policies: Vec<ACLTokenPolicyLink>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub roles: Vec<ACLTokenRoleLink>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub service_identities: Vec<ACLServiceIdentity>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub node_identities: Vec<ACLNodeIdentity>,
#[serde(default)]
pub local: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration_time: Option<String>,
#[serde(rename = "ExpirationTTL")]
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration_ttl: Option<String>,
#[serde(default)]
pub hash: String,
#[serde(default)]
pub create_index: u64,
#[serde(default)]
pub modify_index: u64,
}
impl ACLToken {
pub fn new() -> Self {
Self {
accessor_id: None,
secret_id: None,
description: String::new(),
policies: Vec::new(),
roles: Vec::new(),
service_identities: Vec::new(),
node_identities: Vec::new(),
local: false,
expiration_time: None,
expiration_ttl: None,
hash: String::new(),
create_index: 0,
modify_index: 0,
}
}
pub fn with_description(mut self, description: &str) -> Self {
self.description = description.to_string();
self
}
pub fn with_policy(mut self, policy: ACLTokenPolicyLink) -> Self {
self.policies.push(policy);
self
}
pub fn with_role(mut self, role: ACLTokenRoleLink) -> Self {
self.roles.push(role);
self
}
pub fn with_service_identity(mut self, identity: ACLServiceIdentity) -> Self {
self.service_identities.push(identity);
self
}
pub fn local_only(mut self) -> Self {
self.local = true;
self
}
}
impl Default for ACLToken {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLServiceIdentity {
pub service_name: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub datacenters: Vec<String>,
}
impl ACLServiceIdentity {
pub fn new(service_name: &str) -> Self {
Self {
service_name: service_name.to_string(),
datacenters: Vec::new(),
}
}
pub fn with_datacenters(mut self, datacenters: Vec<String>) -> Self {
self.datacenters = datacenters;
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLNodeIdentity {
pub node_name: String,
pub datacenter: String,
}
impl ACLNodeIdentity {
pub fn new(node_name: &str, datacenter: &str) -> Self {
Self {
node_name: node_name.to_string(),
datacenter: datacenter.to_string(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLRole {
#[serde(rename = "ID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub name: String,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub description: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub policies: Vec<ACLTokenPolicyLink>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub service_identities: Vec<ACLServiceIdentity>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub node_identities: Vec<ACLNodeIdentity>,
#[serde(default)]
pub hash: String,
#[serde(default)]
pub create_index: u64,
#[serde(default)]
pub modify_index: u64,
}
impl ACLRole {
pub fn new(name: &str) -> Self {
Self {
id: None,
name: name.to_string(),
description: String::new(),
policies: Vec::new(),
service_identities: Vec::new(),
node_identities: Vec::new(),
hash: String::new(),
create_index: 0,
modify_index: 0,
}
}
pub fn with_description(mut self, description: &str) -> Self {
self.description = description.to_string();
self
}
pub fn with_policy(mut self, policy: ACLTokenPolicyLink) -> Self {
self.policies.push(policy);
self
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ACLReplicationStatus {
pub enabled: bool,
pub running: bool,
pub source_datacenter: String,
pub replicated_index: u64,
pub replicated_token_index: u64,
pub last_success: String,
pub last_error: String,
}
pub struct ACL {
client: Arc<HttpClient>,
}
impl ACL {
pub fn new(client: Arc<HttpClient>) -> Self {
Self { client }
}
pub async fn token_create(&self, token: &ACLToken, opts: Option<&WriteOptions>) -> Result<(ACLToken, WriteMeta)> {
let mut builder = self.client.put("/v1/acl/token").json(token);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn token_update(&self, token: &ACLToken, opts: Option<&WriteOptions>) -> Result<(ACLToken, WriteMeta)> {
let accessor_id = token.accessor_id.as_ref().ok_or_else(|| {
crate::error::ConsulError::InvalidConfig("accessor_id is required for update".to_string())
})?;
let path = format!("/v1/acl/token/{}", accessor_id);
let mut builder = self.client.put(&path).json(token);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn token_clone(&self, accessor_id: &str, description: Option<&str>, opts: Option<&WriteOptions>) -> Result<(ACLToken, WriteMeta)> {
let path = format!("/v1/acl/token/{}/clone", accessor_id);
let body = if let Some(desc) = description {
serde_json::json!({"Description": desc})
} else {
serde_json::json!({})
};
let mut builder = self.client.put(&path).json(&body);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn token_delete(&self, accessor_id: &str, opts: Option<&WriteOptions>) -> Result<(bool, WriteMeta)> {
let path = format!("/v1/acl/token/{}", accessor_id);
let mut builder = self.client.delete(&path);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write_bool(builder).await
}
pub async fn token_read(&self, accessor_id: &str, opts: Option<&QueryOptions>) -> Result<(ACLToken, QueryMeta)> {
let path = format!("/v1/acl/token/{}", accessor_id);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn token_read_self(&self, opts: Option<&QueryOptions>) -> Result<(ACLToken, QueryMeta)> {
let mut builder = self.client.get("/v1/acl/token/self");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn token_list(&self, opts: Option<&QueryOptions>) -> Result<(Vec<ACLToken>, QueryMeta)> {
let mut builder = self.client.get("/v1/acl/tokens");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn policy_create(&self, policy: &ACLPolicy, opts: Option<&WriteOptions>) -> Result<(ACLPolicy, WriteMeta)> {
let mut builder = self.client.put("/v1/acl/policy").json(policy);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn policy_update(&self, policy: &ACLPolicy, opts: Option<&WriteOptions>) -> Result<(ACLPolicy, WriteMeta)> {
let id = policy.id.as_ref().ok_or_else(|| {
crate::error::ConsulError::InvalidConfig("id is required for update".to_string())
})?;
let path = format!("/v1/acl/policy/{}", id);
let mut builder = self.client.put(&path).json(policy);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn policy_delete(&self, id: &str, opts: Option<&WriteOptions>) -> Result<(bool, WriteMeta)> {
let path = format!("/v1/acl/policy/{}", id);
let mut builder = self.client.delete(&path);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write_bool(builder).await
}
pub async fn policy_read(&self, id: &str, opts: Option<&QueryOptions>) -> Result<(ACLPolicy, QueryMeta)> {
let path = format!("/v1/acl/policy/{}", id);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn policy_read_by_name(&self, name: &str, opts: Option<&QueryOptions>) -> Result<(ACLPolicy, QueryMeta)> {
let path = format!("/v1/acl/policy/name/{}", name);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn policy_list(&self, opts: Option<&QueryOptions>) -> Result<(Vec<ACLPolicy>, QueryMeta)> {
let mut builder = self.client.get("/v1/acl/policies");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn role_create(&self, role: &ACLRole, opts: Option<&WriteOptions>) -> Result<(ACLRole, WriteMeta)> {
let mut builder = self.client.put("/v1/acl/role").json(role);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn role_update(&self, role: &ACLRole, opts: Option<&WriteOptions>) -> Result<(ACLRole, WriteMeta)> {
let id = role.id.as_ref().ok_or_else(|| {
crate::error::ConsulError::InvalidConfig("id is required for update".to_string())
})?;
let path = format!("/v1/acl/role/{}", id);
let mut builder = self.client.put(&path).json(role);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write(builder).await
}
pub async fn role_delete(&self, id: &str, opts: Option<&WriteOptions>) -> Result<(bool, WriteMeta)> {
let path = format!("/v1/acl/role/{}", id);
let mut builder = self.client.delete(&path);
if let Some(opts) = opts {
builder = self.client.apply_write_options(builder, opts);
}
self.client.write_bool(builder).await
}
pub async fn role_read(&self, id: &str, opts: Option<&QueryOptions>) -> Result<(ACLRole, QueryMeta)> {
let path = format!("/v1/acl/role/{}", id);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn role_read_by_name(&self, name: &str, opts: Option<&QueryOptions>) -> Result<(ACLRole, QueryMeta)> {
let path = format!("/v1/acl/role/name/{}", name);
let mut builder = self.client.get(&path);
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn role_list(&self, opts: Option<&QueryOptions>) -> Result<(Vec<ACLRole>, QueryMeta)> {
let mut builder = self.client.get("/v1/acl/roles");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
pub async fn bootstrap(&self) -> Result<(ACLToken, WriteMeta)> {
let builder = self.client.put("/v1/acl/bootstrap");
self.client.write(builder).await
}
pub async fn replication(&self, opts: Option<&QueryOptions>) -> Result<(ACLReplicationStatus, QueryMeta)> {
let mut builder = self.client.get("/v1/acl/replication");
if let Some(opts) = opts {
builder = self.client.apply_query_options(builder, opts);
}
self.client.query(builder).await
}
}