feat: add exact Plugin installation operation resource
This commit is contained in:
@@ -59,6 +59,10 @@ pub const MAX_SCOPED_PERMISSIONS: usize = 256;
|
|||||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
||||||
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
||||||
pub enum PermissionResource {
|
pub enum PermissionResource {
|
||||||
|
PluginInstallation {
|
||||||
|
workspace_id: String,
|
||||||
|
installation_id: String,
|
||||||
|
},
|
||||||
Job {
|
Job {
|
||||||
workspace_id: String,
|
workspace_id: String,
|
||||||
job_id: String,
|
job_id: String,
|
||||||
@@ -135,6 +139,10 @@ impl<'de> Deserialize<'de> for PermissionResource {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
||||||
enum Wire {
|
enum Wire {
|
||||||
|
PluginInstallation {
|
||||||
|
workspace_id: String,
|
||||||
|
installation_id: String,
|
||||||
|
},
|
||||||
Job {
|
Job {
|
||||||
workspace_id: String,
|
workspace_id: String,
|
||||||
job_id: String,
|
job_id: String,
|
||||||
@@ -202,6 +210,13 @@ impl<'de> Deserialize<'de> for PermissionResource {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(match Wire::deserialize(deserializer)? {
|
Ok(match Wire::deserialize(deserializer)? {
|
||||||
|
Wire::PluginInstallation {
|
||||||
|
workspace_id,
|
||||||
|
installation_id,
|
||||||
|
} => Self::PluginInstallation {
|
||||||
|
workspace_id,
|
||||||
|
installation_id,
|
||||||
|
},
|
||||||
Wire::Job {
|
Wire::Job {
|
||||||
workspace_id,
|
workspace_id,
|
||||||
job_id,
|
job_id,
|
||||||
@@ -311,7 +326,8 @@ impl PermissionResource {
|
|||||||
/// Checks Workspace membership without a target existence or data lookup.
|
/// Checks Workspace membership without a target existence or data lookup.
|
||||||
pub fn workspace_id(&self) -> Option<&str> {
|
pub fn workspace_id(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Job { workspace_id, .. }
|
Self::PluginInstallation { workspace_id, .. }
|
||||||
|
| Self::Job { workspace_id, .. }
|
||||||
| Self::Ticket { workspace_id, .. }
|
| Self::Ticket { workspace_id, .. }
|
||||||
| Self::Objective { workspace_id, .. }
|
| Self::Objective { workspace_id, .. }
|
||||||
| Self::MergeRequest { workspace_id, .. }
|
| Self::MergeRequest { workspace_id, .. }
|
||||||
@@ -329,6 +345,13 @@ impl PermissionResource {
|
|||||||
|
|
||||||
pub fn validate(&self) -> Result<(), String> {
|
pub fn validate(&self) -> Result<(), String> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::PluginInstallation {
|
||||||
|
workspace_id,
|
||||||
|
installation_id,
|
||||||
|
} => {
|
||||||
|
validate_authorization_identifier(workspace_id)?;
|
||||||
|
validate_authorization_identifier(installation_id)
|
||||||
|
}
|
||||||
Self::Job {
|
Self::Job {
|
||||||
workspace_id,
|
workspace_id,
|
||||||
job_id,
|
job_id,
|
||||||
@@ -452,6 +475,16 @@ impl PermissionResource {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(
|
||||||
|
Self::PluginInstallation {
|
||||||
|
workspace_id: a,
|
||||||
|
installation_id: ai,
|
||||||
|
},
|
||||||
|
Self::PluginInstallation {
|
||||||
|
workspace_id: b,
|
||||||
|
installation_id: bi,
|
||||||
|
},
|
||||||
|
) => a == b && ai == bi,
|
||||||
(
|
(
|
||||||
Self::Job {
|
Self::Job {
|
||||||
workspace_id: a,
|
workspace_id: a,
|
||||||
@@ -864,6 +897,56 @@ pub fn validate_relative_permission_path(path: &str) -> Result<(), String> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plugin_installation_permissions_require_exact_workspace_and_installation() {
|
||||||
|
let resource = PermissionResource::PluginInstallation {
|
||||||
|
workspace_id: "ws".into(),
|
||||||
|
installation_id: "install".into(),
|
||||||
|
};
|
||||||
|
assert_eq!(resource.workspace_id(), Some("ws"));
|
||||||
|
let wire = serde_json::json!({"kind":"plugin_installation","workspace_id":"ws","installation_id":"install"});
|
||||||
|
assert_eq!(serde_json::to_value(&resource).unwrap(), wire);
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::from_value::<PermissionResource>(wire.clone()).unwrap(),
|
||||||
|
resource
|
||||||
|
);
|
||||||
|
assert!(resource.covers(&resource));
|
||||||
|
for other in [
|
||||||
|
PermissionResource::PluginInstallation {
|
||||||
|
workspace_id: "other".into(),
|
||||||
|
installation_id: "install".into(),
|
||||||
|
},
|
||||||
|
PermissionResource::PluginInstallation {
|
||||||
|
workspace_id: "ws".into(),
|
||||||
|
installation_id: "other".into(),
|
||||||
|
},
|
||||||
|
PermissionResource::Workspace {
|
||||||
|
workspace_id: "ws".into(),
|
||||||
|
},
|
||||||
|
PermissionResource::Feature,
|
||||||
|
] {
|
||||||
|
assert!(!resource.covers(&other));
|
||||||
|
assert!(!other.covers(&resource));
|
||||||
|
}
|
||||||
|
for (field, value) in [
|
||||||
|
("workspace_id", ""),
|
||||||
|
("installation_id", "*"),
|
||||||
|
("installation_id", " install"),
|
||||||
|
] {
|
||||||
|
let mut invalid = wire.clone();
|
||||||
|
invalid[field] = value.into();
|
||||||
|
assert!(
|
||||||
|
serde_json::from_value::<PermissionResource>(invalid)
|
||||||
|
.unwrap()
|
||||||
|
.validate()
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let mut claims = wire;
|
||||||
|
claims["subject"] = "owner".into();
|
||||||
|
assert!(serde_json::from_value::<PermissionResource>(claims).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
fn workdir(path: &str, recursive: bool) -> PermissionResource {
|
fn workdir(path: &str, recursive: bool) -> PermissionResource {
|
||||||
PermissionResource::Workdir {
|
PermissionResource::Workdir {
|
||||||
working_directory_id: "wd".into(),
|
working_directory_id: "wd".into(),
|
||||||
|
|||||||
+22
-1
@@ -7025,6 +7025,27 @@
|
|||||||
},
|
},
|
||||||
"PermissionResource": {
|
"PermissionResource": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"installation_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"const": "plugin_installation",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"workspace_id": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"workspace_id",
|
||||||
|
"installation_id"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -19114,7 +19135,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Yoi Server API",
|
"title": "Yoi Server API",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"x-yoi-source-digest": "sha256:6f738263ef6d2c939d021e719e7d8986edea9389d5ae4805f275b36a9cf722d3"
|
"x-yoi-source-digest": "sha256:709b2f3a7004d11109fc14a2826d4ffffadfd4818698f550ce9150a36442f494"
|
||||||
},
|
},
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:6f738263ef6d2c939d021e719e7d8986edea9389d5ae4805f275b36a9cf722d3)
|
// Input contract: 0.1.0 (sha256:709b2f3a7004d11109fc14a2826d4ffffadfd4818698f550ce9150a36442f494)
|
||||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||||
@@ -35,7 +35,7 @@ export type PasteArtifactMediaType = "text_plain_utf8";
|
|||||||
|
|
||||||
export type PasteArtifactRef = { artifact_id: string; availability: PasteArtifactAvailability; byte_len: number; char_count: number; created_at_ms: number; line_count: number; media_type: PasteArtifactMediaType; sha256: string; source_entry_id: string; };
|
export type PasteArtifactRef = { artifact_id: string; availability: PasteArtifactAvailability; byte_len: number; char_count: number; created_at_ms: number; line_count: number; media_type: PasteArtifactMediaType; sha256: string; source_entry_id: string; };
|
||||||
|
|
||||||
export type PermissionResource = { job_id: string; kind: "job"; workspace_id: string; } | { kind: "ticket"; ticket_id: string; workspace_id: string; } | { kind: "objective"; objective_id: string; workspace_id: string; } | { kind: "merge_request"; merge_request_id: string; workspace_id: string; } | { kind: "subject"; subject_id: string; workspace_id: string; } | { kind: "drive_entry"; node_id: string; workspace_id: string; } | { kind: "drive_request"; request_id: string; workspace_id: string; } | { kind: "repository"; repository_key: string; workspace_id: string; } | { connection_id: string; kind: "workdir_attachment"; workspace_id: string; } | { kind: "workspace_config"; path: string; recursive: boolean; workspace_id: string; } | { kind: "network"; url: string; } | { kind: "sub_worker"; name: string; runtime_id: string; worker_id: string; } | { kind: "feature"; } | { kind: "workspace"; workspace_id: string; } | { kind: "workdir"; path: string; recursive: boolean; working_directory_id: string; } | { kind: "worker_operation"; workspace_id: string; } | { kind: "worker"; runtime_id: string; worker_id: string; };
|
export type PermissionResource = { installation_id: string; kind: "plugin_installation"; workspace_id: string; } | { job_id: string; kind: "job"; workspace_id: string; } | { kind: "ticket"; ticket_id: string; workspace_id: string; } | { kind: "objective"; objective_id: string; workspace_id: string; } | { kind: "merge_request"; merge_request_id: string; workspace_id: string; } | { kind: "subject"; subject_id: string; workspace_id: string; } | { kind: "drive_entry"; node_id: string; workspace_id: string; } | { kind: "drive_request"; request_id: string; workspace_id: string; } | { kind: "repository"; repository_key: string; workspace_id: string; } | { connection_id: string; kind: "workdir_attachment"; workspace_id: string; } | { kind: "workspace_config"; path: string; recursive: boolean; workspace_id: string; } | { kind: "network"; url: string; } | { kind: "sub_worker"; name: string; runtime_id: string; worker_id: string; } | { kind: "feature"; } | { kind: "workspace"; workspace_id: string; } | { kind: "workdir"; path: string; recursive: boolean; working_directory_id: string; } | { kind: "worker_operation"; workspace_id: string; } | { kind: "worker"; runtime_id: string; worker_id: string; };
|
||||||
|
|
||||||
export type RepositoryApiError = { diagnostics?: Array<Diagnostic>; error: string; message: string; };
|
export type RepositoryApiError = { diagnostics?: Array<Diagnostic>; error: string; message: string; };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user