//! 内部 RPC 的 space 域:把资源 id 解析到目录后复用权限内核(Access/Capability)。 use internal::Gate; use internal::server::InternalRouter; use proto::internal::v1::issue_authorization_service_server::{ IssueAuthorizationService as IssueAuthorizationTrait, IssueAuthorizationServiceServer, }; use proto::internal::v1::pull_authorization_service_server::{ PullAuthorizationService as PullAuthorizationTrait, PullAuthorizationServiceServer, }; use proto::internal::v1::space_authorization_service_server::{ SpaceAuthorizationService as SpaceAuthorizationTrait, SpaceAuthorizationServiceServer, }; use proto::internal::v1::{ AuthorizationDecision, AuthorizeIssueRequest, AuthorizePullRequest, AuthorizeResourceRequest, SpaceMembersRequest, SpaceMembersResponse, }; use tonic::{Request, Response, Status}; use crate::errors::SpaceError; use crate::services::service::SpaceService; mod space_directory; pub fn register(router: InternalRouter, service: &SpaceService, gate: Gate) -> InternalRouter { let rpc = SpaceRpc { service: service.clone(), }; let router = space_directory::register(router, service, gate.clone()); let router = router.add_service(IssueAuthorizationServiceServer::with_interceptor( rpc.clone(), gate.clone(), )); let router = router.add_service(PullAuthorizationServiceServer::with_interceptor( rpc.clone(), gate.clone(), )); let router = router.add_service(SpaceAuthorizationServiceServer::with_interceptor( rpc.clone(), gate.clone(), )); router } #[derive(Clone)] struct SpaceRpc { service: SpaceService, } fn resource_id(value: &str) -> Result { value .parse() .map_err(|_| Status::invalid_argument("resource id must be a UUID")) } #[tonic::async_trait] impl IssueAuthorizationTrait for SpaceRpc { async fn authorize_issue( &self, request: Request, ) -> Result, Status> { let _meta = internal::interceptor::meta(&request)?; let request = request.into_inner(); let issue_id = resource_id(&request.issue_id)?; let allowed = self .service .authorize_internal_issue(request.user_id, issue_id) .await .map_err(status_of)?; Ok(Response::new(AuthorizationDecision { allowed, reason: String::new(), resource_id: request.issue_id, })) } } #[tonic::async_trait] impl PullAuthorizationTrait for SpaceRpc { async fn authorize_pull( &self, request: Request, ) -> Result, Status> { let _meta = internal::interceptor::meta(&request)?; let request = request.into_inner(); let pull_id = resource_id(&request.pull_id)?; let allowed = self .service .authorize_internal_pull(request.user_id, pull_id) .await .map_err(status_of)?; Ok(Response::new(AuthorizationDecision { allowed, reason: String::new(), resource_id: request.pull_id, })) } } #[tonic::async_trait] impl SpaceAuthorizationTrait for SpaceRpc { async fn authorize_repo( &self, request: Request, ) -> Result, Status> { let _meta = internal::interceptor::meta(&request)?; let request = request.into_inner(); let repo_id = resource_id(&request.repo_id)?; let allowed = self .service .authorize_internal_repo(request.user_id, repo_id) .await .map_err(status_of)?; Ok(Response::new(AuthorizationDecision { allowed, reason: String::new(), resource_id: request.repo_id, })) } async fn authorize_space( &self, request: Request, ) -> Result, Status> { let _meta = internal::interceptor::meta(&request)?; let request = request.into_inner(); let space_id = resource_id(&request.space_id)?; let allowed = self .service .authorize_internal_space(request.user_id, space_id) .await .map_err(status_of)?; Ok(Response::new(AuthorizationDecision { allowed, reason: String::new(), resource_id: request.space_id, })) } async fn list_members( &self, request: Request, ) -> Result, Status> { let _meta = internal::interceptor::meta(&request)?; let request = request.into_inner(); let space_id = resource_id(&request.space_id)?; let user_ids = self .service .internal_space_member_ids(space_id) .await .map_err(status_of)?; Ok(Response::new(SpaceMembersResponse { user_ids })) } } fn status_of(error: SpaceError) -> Status { match error { SpaceError::Unauthorized => Status::unauthenticated("space authorization rejected"), SpaceError::Forbidden | SpaceError::NotFound | SpaceError::SpaceNotFound => { Status::permission_denied("not allowed") } SpaceError::BadRequest(message) => Status::invalid_argument(message), other => { tracing::warn!(%other, "internal space authorization failed"); Status::internal("space authorization failed") } } }