tape: finish api permission checks
This commit is contained in:
parent
ee33795b72
commit
b4975d3102
@ -20,7 +20,9 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
cached_user_info::CachedUserInfo,
|
cached_user_info::CachedUserInfo,
|
||||||
acl::{
|
acl::{
|
||||||
|
PRIV_DATASTORE_READ,
|
||||||
PRIV_TAPE_AUDIT,
|
PRIV_TAPE_AUDIT,
|
||||||
|
PRIV_TAPE_WRITE,
|
||||||
},
|
},
|
||||||
tape_job::{
|
tape_job::{
|
||||||
TapeBackupJobConfig,
|
TapeBackupJobConfig,
|
||||||
@ -71,6 +73,33 @@ pub const ROUTER: Router = Router::new()
|
|||||||
.post(&API_METHOD_BACKUP)
|
.post(&API_METHOD_BACKUP)
|
||||||
.match_all("id", &TAPE_BACKUP_JOB_ROUTER);
|
.match_all("id", &TAPE_BACKUP_JOB_ROUTER);
|
||||||
|
|
||||||
|
fn check_backup_permission(
|
||||||
|
auth_id: &Authid,
|
||||||
|
store: &str,
|
||||||
|
pool: &str,
|
||||||
|
drive: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
|
||||||
|
let user_info = CachedUserInfo::new()?;
|
||||||
|
|
||||||
|
let privs = user_info.lookup_privs(auth_id, &["datastore", store]);
|
||||||
|
if (privs & PRIV_DATASTORE_READ) == 0 {
|
||||||
|
bail!("no permissions on /datastore/{}", store);
|
||||||
|
}
|
||||||
|
|
||||||
|
let privs = user_info.lookup_privs(auth_id, &["tape", "drive", drive]);
|
||||||
|
if (privs & PRIV_TAPE_WRITE) == 0 {
|
||||||
|
bail!("no permissions on /tape/drive/{}", drive);
|
||||||
|
}
|
||||||
|
|
||||||
|
let privs = user_info.lookup_privs(auth_id, &["tape", "pool", pool]);
|
||||||
|
if (privs & PRIV_TAPE_WRITE) == 0 {
|
||||||
|
bail!("no permissions on /tape/pool/{}", pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
returns: {
|
returns: {
|
||||||
description: "List configured thape backup jobs and their status",
|
description: "List configured thape backup jobs and their status",
|
||||||
@ -202,6 +231,12 @@ pub fn do_tape_backup_job(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
// Note: parameters are from job config, so we need to test inside function body
|
||||||
|
description: "The user needs Tape.Write privilege on /tape/pool/{pool} \
|
||||||
|
and /tape/drive/{drive}, Datastore.Read privilege on /datastore/{store}.",
|
||||||
|
permission: &Permission::Anybody,
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Runs a tape backup job manually.
|
/// Runs a tape backup job manually.
|
||||||
pub fn run_tape_backup_job(
|
pub fn run_tape_backup_job(
|
||||||
@ -213,6 +248,13 @@ pub fn run_tape_backup_job(
|
|||||||
let (config, _digest) = config::tape_job::config()?;
|
let (config, _digest) = config::tape_job::config()?;
|
||||||
let backup_job: TapeBackupJobConfig = config.lookup("backup", &id)?;
|
let backup_job: TapeBackupJobConfig = config.lookup("backup", &id)?;
|
||||||
|
|
||||||
|
check_backup_permission(
|
||||||
|
&auth_id,
|
||||||
|
&backup_job.setup.store,
|
||||||
|
&backup_job.setup.pool,
|
||||||
|
&backup_job.setup.drive,
|
||||||
|
)?;
|
||||||
|
|
||||||
let job = Job::new("tape-backup-job", &id)?;
|
let job = Job::new("tape-backup-job", &id)?;
|
||||||
|
|
||||||
let upid_str = do_tape_backup_job(job, backup_job.setup, &auth_id, None)?;
|
let upid_str = do_tape_backup_job(job, backup_job.setup, &auth_id, None)?;
|
||||||
@ -232,6 +274,12 @@ pub fn run_tape_backup_job(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
// Note: parameters are no uri parameter, so we need to test inside function body
|
||||||
|
description: "The user needs Tape.Write privilege on /tape/pool/{pool} \
|
||||||
|
and /tape/drive/{drive}, Datastore.Read privilege on /datastore/{store}.",
|
||||||
|
permission: &Permission::Anybody,
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Backup datastore to tape media pool
|
/// Backup datastore to tape media pool
|
||||||
pub fn backup(
|
pub fn backup(
|
||||||
@ -241,6 +289,13 @@ pub fn backup(
|
|||||||
|
|
||||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||||
|
|
||||||
|
check_backup_permission(
|
||||||
|
&auth_id,
|
||||||
|
&setup.store,
|
||||||
|
&setup.pool,
|
||||||
|
&setup.drive,
|
||||||
|
)?;
|
||||||
|
|
||||||
let datastore = DataStore::lookup_datastore(&setup.store)?;
|
let datastore = DataStore::lookup_datastore(&setup.store)?;
|
||||||
|
|
||||||
let (config, _digest) = config::media_pool::config()?;
|
let (config, _digest) = config::media_pool::config()?;
|
||||||
|
@ -13,6 +13,7 @@ use crate::{
|
|||||||
cached_user_info::CachedUserInfo,
|
cached_user_info::CachedUserInfo,
|
||||||
acl::{
|
acl::{
|
||||||
PRIV_TAPE_AUDIT,
|
PRIV_TAPE_AUDIT,
|
||||||
|
PRIV_TAPE_READ,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api2::types::{
|
api2::types::{
|
||||||
@ -60,6 +61,9 @@ use crate::{
|
|||||||
type: MtxStatusEntry,
|
type: MtxStatusEntry,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Get tape changer status
|
/// Get tape changer status
|
||||||
pub async fn get_status(
|
pub async fn get_status(
|
||||||
@ -156,6 +160,9 @@ pub async fn get_status(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Transfers media from one slot to another
|
/// Transfers media from one slot to another
|
||||||
pub async fn transfer(
|
pub async fn transfer(
|
||||||
|
@ -29,6 +29,8 @@ use crate::{
|
|||||||
cached_user_info::CachedUserInfo,
|
cached_user_info::CachedUserInfo,
|
||||||
acl::{
|
acl::{
|
||||||
PRIV_TAPE_AUDIT,
|
PRIV_TAPE_AUDIT,
|
||||||
|
PRIV_TAPE_READ,
|
||||||
|
PRIV_TAPE_WRITE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api2::{
|
api2::{
|
||||||
@ -143,6 +145,9 @@ where
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Load media with specified label
|
/// Load media with specified label
|
||||||
///
|
///
|
||||||
@ -182,6 +187,9 @@ pub fn load_media(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Load media from the specified slot
|
/// Load media from the specified slot
|
||||||
///
|
///
|
||||||
@ -215,6 +223,9 @@ pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
|
|||||||
type: u64,
|
type: u64,
|
||||||
minimum: 1,
|
minimum: 1,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Export media with specified label
|
/// Export media with specified label
|
||||||
pub async fn export_media(drive: String, label_text: String) -> Result<u64, Error> {
|
pub async fn export_media(drive: String, label_text: String) -> Result<u64, Error> {
|
||||||
@ -252,6 +263,9 @@ pub async fn export_media(drive: String, label_text: String) -> Result<u64, Erro
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Unload media via changer
|
/// Unload media via changer
|
||||||
pub fn unload(
|
pub fn unload(
|
||||||
@ -297,6 +311,9 @@ pub fn unload(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Erase media. Check for label-text if given (cancels if wrong media).
|
/// Erase media. Check for label-text if given (cancels if wrong media).
|
||||||
pub fn erase_media(
|
pub fn erase_media(
|
||||||
@ -381,6 +398,9 @@ pub fn erase_media(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Rewind tape
|
/// Rewind tape
|
||||||
pub fn rewind(
|
pub fn rewind(
|
||||||
@ -413,6 +433,9 @@ pub fn rewind(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Eject/Unload drive media
|
/// Eject/Unload drive media
|
||||||
pub fn eject_media(
|
pub fn eject_media(
|
||||||
@ -456,6 +479,9 @@ pub fn eject_media(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Label media
|
/// Label media
|
||||||
///
|
///
|
||||||
@ -588,6 +614,9 @@ fn write_media_label(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Try to restore a tape encryption key
|
/// Try to restore a tape encryption key
|
||||||
pub async fn restore_key(
|
pub async fn restore_key(
|
||||||
@ -631,6 +660,9 @@ pub async fn restore_key(
|
|||||||
returns: {
|
returns: {
|
||||||
type: MediaIdFlat,
|
type: MediaIdFlat,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Read media label (optionally inventorize media)
|
/// Read media label (optionally inventorize media)
|
||||||
pub async fn read_label(
|
pub async fn read_label(
|
||||||
@ -706,6 +738,9 @@ pub async fn read_label(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Clean drive
|
/// Clean drive
|
||||||
pub fn clean_drive(
|
pub fn clean_drive(
|
||||||
@ -748,6 +783,9 @@ pub fn clean_drive(
|
|||||||
type: LabelUuidMap,
|
type: LabelUuidMap,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// List known media labels (Changer Inventory)
|
/// List known media labels (Changer Inventory)
|
||||||
///
|
///
|
||||||
@ -817,6 +855,9 @@ pub async fn inventory(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Update inventory
|
/// Update inventory
|
||||||
///
|
///
|
||||||
@ -911,6 +952,9 @@ pub fn update_inventory(
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Label media with barcodes from changer device
|
/// Label media with barcodes from changer device
|
||||||
pub fn barcode_label_media(
|
pub fn barcode_label_media(
|
||||||
@ -1020,6 +1064,9 @@ fn barcode_label_media_worker(
|
|||||||
type: MamAttribute,
|
type: MamAttribute,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Read Cartridge Memory (Medium auxiliary memory attributes)
|
/// Read Cartridge Memory (Medium auxiliary memory attributes)
|
||||||
pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
|
pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
|
||||||
@ -1047,6 +1094,9 @@ pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error>
|
|||||||
returns: {
|
returns: {
|
||||||
type: Lp17VolumeStatistics,
|
type: Lp17VolumeStatistics,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Read Volume Statistics (SCSI log page 17h)
|
/// Read Volume Statistics (SCSI log page 17h)
|
||||||
pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
|
pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
|
||||||
@ -1074,6 +1124,9 @@ pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Er
|
|||||||
returns: {
|
returns: {
|
||||||
type: LinuxDriveAndMediaStatus,
|
type: LinuxDriveAndMediaStatus,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Get drive/media status
|
/// Get drive/media status
|
||||||
pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
|
pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
|
||||||
@ -1115,6 +1168,9 @@ pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Scan media and record content
|
/// Scan media and record content
|
||||||
pub fn catalog_media(
|
pub fn catalog_media(
|
||||||
|
@ -11,6 +11,7 @@ use proxmox::{
|
|||||||
RpcEnvironment,
|
RpcEnvironment,
|
||||||
RpcEnvironmentType,
|
RpcEnvironmentType,
|
||||||
Router,
|
Router,
|
||||||
|
Permission,
|
||||||
section_config::SectionConfigData,
|
section_config::SectionConfigData,
|
||||||
},
|
},
|
||||||
tools::{
|
tools::{
|
||||||
@ -33,7 +34,14 @@ use crate::{
|
|||||||
UPID_SCHEMA,
|
UPID_SCHEMA,
|
||||||
Authid,
|
Authid,
|
||||||
},
|
},
|
||||||
config,
|
config::{
|
||||||
|
self,
|
||||||
|
cached_user_info::CachedUserInfo,
|
||||||
|
acl::{
|
||||||
|
PRIV_DATASTORE_BACKUP,
|
||||||
|
PRIV_TAPE_READ,
|
||||||
|
},
|
||||||
|
},
|
||||||
backup::{
|
backup::{
|
||||||
archive_type,
|
archive_type,
|
||||||
MANIFEST_BLOB_NAME,
|
MANIFEST_BLOB_NAME,
|
||||||
@ -76,7 +84,6 @@ use crate::{
|
|||||||
pub const ROUTER: Router = Router::new()
|
pub const ROUTER: Router = Router::new()
|
||||||
.post(&API_METHOD_RESTORE);
|
.post(&API_METHOD_RESTORE);
|
||||||
|
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
@ -95,6 +102,12 @@ pub const ROUTER: Router = Router::new()
|
|||||||
returns: {
|
returns: {
|
||||||
schema: UPID_SCHEMA,
|
schema: UPID_SCHEMA,
|
||||||
},
|
},
|
||||||
|
access: {
|
||||||
|
// Note: parameters are no uri parameter, so we need to test inside function body
|
||||||
|
description: "The user needs Tape.Read privilege on /tape/pool/{pool} \
|
||||||
|
and /tape/drive/{drive}, Datastore.Backup privilege on /datastore/{store}.",
|
||||||
|
permission: &Permission::Anybody,
|
||||||
|
},
|
||||||
)]
|
)]
|
||||||
/// Restore data from media-set
|
/// Restore data from media-set
|
||||||
pub fn restore(
|
pub fn restore(
|
||||||
@ -104,9 +117,18 @@ pub fn restore(
|
|||||||
rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
|
|
||||||
let datastore = DataStore::lookup_datastore(&store)?;
|
|
||||||
|
|
||||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||||
|
let user_info = CachedUserInfo::new()?;
|
||||||
|
|
||||||
|
let privs = user_info.lookup_privs(&auth_id, &["datastore", &store]);
|
||||||
|
if (privs & PRIV_DATASTORE_BACKUP) == 0 {
|
||||||
|
bail!("no permissions on /datastore/{}", store);
|
||||||
|
}
|
||||||
|
|
||||||
|
let privs = user_info.lookup_privs(&auth_id, &["tape", "drive", &drive]);
|
||||||
|
if (privs & PRIV_TAPE_READ) == 0 {
|
||||||
|
bail!("no permissions on /tape/drive/{}", drive);
|
||||||
|
}
|
||||||
|
|
||||||
let status_path = Path::new(TAPE_STATUS_DIR);
|
let status_path = Path::new(TAPE_STATUS_DIR);
|
||||||
let inventory = Inventory::load(status_path)?;
|
let inventory = Inventory::load(status_path)?;
|
||||||
@ -115,6 +137,13 @@ pub fn restore(
|
|||||||
|
|
||||||
let pool = inventory.lookup_media_set_pool(&media_set_uuid)?;
|
let pool = inventory.lookup_media_set_pool(&media_set_uuid)?;
|
||||||
|
|
||||||
|
let privs = user_info.lookup_privs(&auth_id, &["tape", "pool", &pool]);
|
||||||
|
if (privs & PRIV_TAPE_READ) == 0 {
|
||||||
|
bail!("no permissions on /tape/pool/{}", pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
let datastore = DataStore::lookup_datastore(&store)?;
|
||||||
|
|
||||||
let (drive_config, _digest) = config::drive::config()?;
|
let (drive_config, _digest) = config::drive::config()?;
|
||||||
|
|
||||||
// early check/lock before starting worker
|
// early check/lock before starting worker
|
||||||
|
Loading…
Reference in New Issue
Block a user