core: add community policy engine seam
This commit is contained in:
@@ -144,5 +144,6 @@ Progress:
|
|||||||
- done:
|
- done:
|
||||||
- Phase 0 / task `0.1`: создан `crank-core::ext` module skeleton
|
- Phase 0 / task `0.1`: создан `crank-core::ext` module skeleton
|
||||||
- Phase 0 / task `0.2`: `MachineCredentialVerifier` и связанные типы вынесены из `apps/mcp-server` в public seam `crank_core::ext::auth`
|
- Phase 0 / task `0.2`: `MachineCredentialVerifier` и связанные типы вынесены из `apps/mcp-server` в public seam `crank_core::ext::auth`
|
||||||
|
- Phase 0 / task `0.5`: введены `PolicyEngine`, `SessionActor`, `PolicyAction`, `PolicyScope`, `PolicyDecision` и default `OwnerOnlyPolicyEngine` в public seam `crank_core::ext::access`
|
||||||
- Phase 0 / task `0.7`: введены `CapabilityProfile` и `CommunityCapabilityProfile`, а `admin-api` capability payload теперь собирается через public seam вместо локального literal
|
- Phase 0 / task `0.7`: введены `CapabilityProfile` и `CommunityCapabilityProfile`, а `admin-api` capability payload теперь собирается через public seam вместо локального literal
|
||||||
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
- backward-compatible alias `CommunityMachineCredentialVerifier` сохранен, поведение Community не изменено
|
||||||
|
|||||||
@@ -1 +1,142 @@
|
|||||||
|
use crate::{MembershipRole, UserId, WorkspaceId};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct SessionActor {
|
||||||
|
pub user_id: UserId,
|
||||||
|
pub workspace_id: WorkspaceId,
|
||||||
|
pub role: MembershipRole,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
pub enum PolicyAction {
|
||||||
|
ReadWorkspace,
|
||||||
|
WriteWorkspace,
|
||||||
|
ReadWorkspaceAccess,
|
||||||
|
WriteWorkspaceAccess,
|
||||||
|
ReadOperation,
|
||||||
|
WriteOperation,
|
||||||
|
ReadAgent,
|
||||||
|
WriteAgent,
|
||||||
|
ReadPlatformApiKey,
|
||||||
|
WritePlatformApiKey,
|
||||||
|
ReadSecret,
|
||||||
|
WriteSecret,
|
||||||
|
ReadAuthProfile,
|
||||||
|
WriteAuthProfile,
|
||||||
|
ReadObservability,
|
||||||
|
ReadCapability,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolicyAction {
|
||||||
|
pub fn is_read(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::ReadWorkspace
|
||||||
|
| Self::ReadWorkspaceAccess
|
||||||
|
| Self::ReadOperation
|
||||||
|
| Self::ReadAgent
|
||||||
|
| Self::ReadPlatformApiKey
|
||||||
|
| Self::ReadSecret
|
||||||
|
| Self::ReadAuthProfile
|
||||||
|
| Self::ReadObservability
|
||||||
|
| Self::ReadCapability
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum PolicyScope {
|
||||||
|
Workspace(WorkspaceId),
|
||||||
|
Global,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum PolicyDecision {
|
||||||
|
Allow,
|
||||||
|
Deny { reason: &'static str },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PolicyEngine: Send + Sync {
|
||||||
|
fn check(
|
||||||
|
&self,
|
||||||
|
actor: &SessionActor,
|
||||||
|
action: PolicyAction,
|
||||||
|
scope: PolicyScope,
|
||||||
|
) -> PolicyDecision;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct OwnerOnlyPolicyEngine;
|
||||||
|
|
||||||
|
impl PolicyEngine for OwnerOnlyPolicyEngine {
|
||||||
|
fn check(
|
||||||
|
&self,
|
||||||
|
actor: &SessionActor,
|
||||||
|
action: PolicyAction,
|
||||||
|
_scope: PolicyScope,
|
||||||
|
) -> PolicyDecision {
|
||||||
|
if matches!(actor.role, MembershipRole::Owner) || action.is_read() {
|
||||||
|
PolicyDecision::Allow
|
||||||
|
} else {
|
||||||
|
PolicyDecision::Deny {
|
||||||
|
reason: "write actions require owner role in community",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{
|
||||||
|
OwnerOnlyPolicyEngine, PolicyAction, PolicyDecision, PolicyEngine, PolicyScope,
|
||||||
|
SessionActor,
|
||||||
|
};
|
||||||
|
use crate::{MembershipRole, UserId, WorkspaceId};
|
||||||
|
|
||||||
|
fn actor(role: MembershipRole) -> SessionActor {
|
||||||
|
SessionActor {
|
||||||
|
user_id: UserId::new("user_01"),
|
||||||
|
workspace_id: WorkspaceId::new("ws_01"),
|
||||||
|
role,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn owner_is_allowed_for_write_actions() {
|
||||||
|
let decision = OwnerOnlyPolicyEngine.check(
|
||||||
|
&actor(MembershipRole::Owner),
|
||||||
|
PolicyAction::WriteWorkspace,
|
||||||
|
PolicyScope::Workspace(WorkspaceId::new("ws_01")),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(decision, PolicyDecision::Allow);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_owner_is_allowed_for_read_actions() {
|
||||||
|
let decision = OwnerOnlyPolicyEngine.check(
|
||||||
|
&actor(MembershipRole::Viewer),
|
||||||
|
PolicyAction::ReadWorkspace,
|
||||||
|
PolicyScope::Workspace(WorkspaceId::new("ws_01")),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(decision, PolicyDecision::Allow);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_owner_is_denied_for_write_actions() {
|
||||||
|
let decision = OwnerOnlyPolicyEngine.check(
|
||||||
|
&actor(MembershipRole::Admin),
|
||||||
|
PolicyAction::WriteWorkspace,
|
||||||
|
PolicyScope::Workspace(WorkspaceId::new("ws_01")),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
decision,
|
||||||
|
PolicyDecision::Deny {
|
||||||
|
reason: "write actions require owner role in community",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ pub use cache::{
|
|||||||
pub use edition::{
|
pub use edition::{
|
||||||
EditionCapabilities, EditionLimits, MachineAccessMode, OperationSecurityLevel, ProductEdition,
|
EditionCapabilities, EditionLimits, MachineAccessMode, OperationSecurityLevel, ProductEdition,
|
||||||
};
|
};
|
||||||
|
pub use ext::access::{
|
||||||
|
OwnerOnlyPolicyEngine, PolicyAction, PolicyDecision, PolicyEngine, PolicyScope, SessionActor,
|
||||||
|
};
|
||||||
pub use ext::auth::{
|
pub use ext::auth::{
|
||||||
MachineCredentialVerifier, MachineCredentialVerifierError, SharedMachineCredentialVerifier,
|
MachineCredentialVerifier, MachineCredentialVerifierError, SharedMachineCredentialVerifier,
|
||||||
VerifiedMachineCredential,
|
VerifiedMachineCredential,
|
||||||
|
|||||||
Reference in New Issue
Block a user