use crate::client::ApiClient;
use crate::config::{Account, Config};
use crate::crypto;
use crate::error::{CliError, Result};
pub fn get_current_account() -> Result<Account> {
let config = Config::load()?;
config
.get_default_account()
.cloned()
.ok_or(CliError::NotLoggedIn)
}
pub async fn save_account(
name: String,
server: String,
username: String,
token: String,
role: String,
set_default: bool,
) -> Result<()> {
save_account_with_role(name, server, username, token, role, set_default).await
}
pub async fn save_account_with_role(
name: String,
server: String,
username: String,
token: String,
role: String,
set_default: bool,
) -> Result<()> {
let mut config = Config::load()?;
let encrypted_token = crypto::encrypt_token(&token)?;
let account = Account {
name: name.clone(),
server,
username,
token: encrypted_token,
default: set_default,
role,
};
config.remove_account(&name);
config.add_account(account);
config.save()?;
Ok(())
}
pub fn switch_account(name: &str) -> Result<()> {
let mut config = Config::load()?;
if !config.set_default_account(name) {
return Err(CliError::NotFound(format!("Account '{}' not found", name)));
}
config.save()?;
Ok(())
}
pub fn remove_account(name: &str) -> Result<()> {
let mut config = Config::load()?;
if !config.remove_account(name) {
return Err(CliError::NotFound(format!("Account '{}' not found", name)));
}
config.save()?;
Ok(())
}
pub fn get_api_client() -> Result<ApiClient> {
let account = get_current_account()?;
let token = crypto::decrypt_token(&account.token)?;
ApiClient::new(account.server, Some(token))
}
pub fn get_api_client_for_server(server: String) -> Result<ApiClient> {
ApiClient::new(server, None)
}
pub async fn verify_current_account() -> Result<bool> {
let client = get_api_client()?;
client.verify_token().await
}
pub async fn require_admin(operation: &str) -> Result<()> {
let account = get_current_account()?;
if account.role == "admin" {
Ok(())
} else {
Err(CliError::role_missing(
account.role.clone(),
"admin",
operation,
))
}
}
pub async fn require_developer(operation: &str) -> Result<()> {
let account = get_current_account()?;
if account.role == "admin" || account.role == "developer" {
Ok(())
} else {
Err(CliError::role_missing(
account.role.clone(),
"developer",
operation,
))
}
}
pub async fn require_role(required_role: &str, operation: &str) -> Result<()> {
let account = get_current_account()?;
if account.role == required_role || account.role == "admin" {
Ok(())
} else {
Err(CliError::role_missing(
account.role.clone(),
required_role,
operation,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Account;
use std::fs;
use tempfile::TempDir;
#[tokio::test]
async fn test_get_api_client_for_server() {
let client = get_api_client_for_server("http://localhost:38080".to_string());
assert!(client.is_ok());
}
#[test]
fn test_require_admin_with_wrong_role() {
let error = CliError::role_missing("developer", "admin", "delete all users");
let error_msg = error.to_string();
assert!(error_msg.contains("developer"));
assert!(error_msg.contains("admin"));
assert!(error_msg.contains("delete all users"));
assert!(error_msg.contains("contact your administrator"));
}
#[test]
fn test_require_developer_with_wrong_role() {
let error = CliError::role_missing("user", "developer", "create app");
let error_msg = error.to_string();
assert!(error_msg.contains("user"));
assert!(error_msg.contains("developer"));
assert!(error_msg.contains("create app"));
}
#[test]
fn test_role_missing_error_for_admin() {
let error = CliError::RoleMissing {
current: "developer".to_string(),
required: "admin".to_string(),
operation: "list all users".to_string(),
hint: "Contact your platform administrator to upgrade your account.".to_string(),
};
let error_msg = error.to_string();
assert!(error_msg.contains("developer"));
assert!(error_msg.contains("admin"));
assert!(error_msg.contains("list all users"));
}
#[test]
fn test_role_missing_error_for_developer() {
let error = CliError::RoleMissing {
current: "user".to_string(),
required: "developer".to_string(),
operation: "create application".to_string(),
hint: "Contact your platform administrator to upgrade your account.".to_string(),
};
let error_msg = error.to_string();
assert!(error_msg.contains("user"));
assert!(error_msg.contains("developer"));
assert!(error_msg.contains("create application"));
}
#[tokio::test]
async fn test_save_account_with_role() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("config.toml");
unsafe {
std::env::set_var("OAUTH_DB_CLI_CONFIG_DIR", temp_dir.path());
}
let config = Config::default();
fs::create_dir_all(temp_dir.path()).unwrap();
fs::write(&config_path, toml::to_string_pretty(&config).unwrap()).unwrap();
let result = save_account_with_role(
"test@localhost".to_string(),
"http://localhost:38080".to_string(),
"test".to_string(),
"test_token".to_string(),
"admin".to_string(),
true,
)
.await;
unsafe {
std::env::remove_var("OAUTH_DB_CLI_CONFIG_DIR");
}
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_account_role_serialization() {
let account = Account {
name: "test@localhost".to_string(),
server: "http://localhost:38080".to_string(),
username: "test".to_string(),
token: "encrypted_token".to_string(),
default: true,
role: "admin".to_string(),
};
let serialized = toml::to_string(&account).unwrap();
assert!(serialized.contains("role = \"admin\""));
}
#[test]
fn test_account_role_deserialization() {
let toml_with_role = r#"
name = "test@localhost"
server = "http://localhost:38080"
username = "test"
token = "encrypted_token"
default = true
role = "admin"
"#;
let account: Account = toml::from_str(toml_with_role).unwrap();
assert_eq!(account.role, "admin");
}
#[test]
fn test_multiple_role_types() {
let admin = Account {
name: "admin@localhost".to_string(),
server: "http://localhost:38080".to_string(),
username: "admin".to_string(),
token: "token".to_string(),
default: true,
role: "admin".to_string(),
};
assert_eq!(&admin.role, "admin");
let developer = Account {
name: "dev@localhost".to_string(),
server: "http://localhost:38080".to_string(),
username: "dev".to_string(),
token: "token".to_string(),
default: false,
role: "developer".to_string(),
};
assert_eq!(&developer.role, "developer");
let user = Account {
name: "user@localhost".to_string(),
server: "http://localhost:38080".to_string(),
username: "user".to_string(),
token: "token".to_string(),
default: false,
role: "user".to_string(),
};
assert_eq!(&user.role, "user");
}
}