Mobile support to come.
use async_trait::async_trait;
use crate::dto::{
AnswerAuthorizationRequest, CommentAuthorizationRequest, GitHttpAuthorizationRequest,
OrganizationAuthorizationRequest, QuestionAuthorizationRequest, RepositoryAuthorizationRequest,
RepositoryCreationAuthorizationRequest,
};
use crate::error::AuthorizationError;
use crate::model::{GitOperation, OrganizationRole, RepositoryOwnerType};
use crate::repository::{
OrganizationRepository, OrganizationRepositoryImpl, QuestionRepository, QuestionRepositoryImpl,
RepositoryRepository, RepositoryRepositoryImpl, UserRepository, UserRepositoryImpl,
};
#[async_trait]
pub trait AuthorizationService: Send + Sync + 'static {
async fn verify_authorized_for_git_http(
&self,
request: GitHttpAuthorizationRequest,
) -> Result<(), AuthorizationError>;
async fn verify_authorized_for_repository_creation(
&self,
request: RepositoryCreationAuthorizationRequest,
) -> Result<(), AuthorizationError>;
async fn verify_authorized_for_repository(
&self,
request: RepositoryAuthorizationRequest,
) -> Result<(), AuthorizationError>;
async fn verify_authorized_for_organization(
&self,
request: OrganizationAuthorizationRequest,
) -> Result<(), AuthorizationError>;
async fn verify_authorized_for_question(
&self,
request: QuestionAuthorizationRequest,
) -> Result<(), AuthorizationError>;
async fn verify_authorized_for_answer(
&self,
request: AnswerAuthorizationRequest,
) -> Result<(), AuthorizationError>;
async fn verify_authorized_for_comment(
&self,
request: CommentAuthorizationRequest,
) -> Result<(), AuthorizationError>;
}
#[derive(Debug, Clone)]
pub struct AuthorizationServiceImpl<O, R, Q, U>
where
O: OrganizationRepository,
R: RepositoryRepository,
Q: QuestionRepository,
U: UserRepository,
{
org_repo: O,
repo_repo: R,
question_repo: Q,
user_repo: U,
}
impl
AuthorizationServiceImpl<
OrganizationRepositoryImpl,
RepositoryRepositoryImpl,
QuestionRepositoryImpl,
UserRepositoryImpl,
>
{
pub fn new(
org_repo: OrganizationRepositoryImpl,
repo_repo: RepositoryRepositoryImpl,
question_repo: QuestionRepositoryImpl,
user_repo: UserRepositoryImpl,
) -> Self {
Self {
org_repo,
repo_repo,
question_repo,
user_repo,
}
}
}
#[async_trait]
impl<O, R, Q, U> AuthorizationService for AuthorizationServiceImpl<O, R, Q, U>
where
O: OrganizationRepository,
R: RepositoryRepository,
Q: QuestionRepository,
U: UserRepository,
{
async fn verify_authorized_for_git_http(
&self,
request: GitHttpAuthorizationRequest,
) -> Result<(), AuthorizationError> {
let repository = self
.repo_repo
.get(request.owner.as_ref(), request.repo.as_ref())
.await?
.ok_or_else(|| AuthorizationError::NotFound("Repository not found".to_string()))?;
if repository.is_public() && request.operation == GitOperation::Read {
return Ok(());
}
let user_id = request.user_id.ok_or(AuthorizationError::Unauthorized)?;
if repository.is_owned_by_user() {
if repository.owner_id != user_id {
return Err(AuthorizationError::Unauthorized);
}
} else {
let is_member = self
.org_repo
.is_member(repository.owner_id, user_id)
.await?;
if !is_member {
return Err(AuthorizationError::Unauthorized);
}
}
Ok(())
}
async fn verify_authorized_for_repository_creation(
&self,
request: RepositoryCreationAuthorizationRequest,
) -> Result<(), AuthorizationError> {
match request.owner_type {
RepositoryOwnerType::User => {
let user = self
.user_repo
.get_by_id(request.user_id)
.await?
.ok_or(AuthorizationError::Unauthorized)?;
if user.name.to_lowercase() != request.owner.as_ref().to_lowercase() {
return Err(AuthorizationError::Unauthorized);
}
}
RepositoryOwnerType::Organization => {
let org = self
.org_repo
.get(request.owner.as_ref())
.await?
.ok_or_else(|| {
AuthorizationError::NotFound(format!(
"Organization not found: {}",
request.owner.as_ref()
))
})?;
let is_member = self.org_repo.is_member(org.id, request.user_id).await?;
if !is_member {
return Err(AuthorizationError::Unauthorized);
}
}
}
Ok(())
}
async fn verify_authorized_for_repository(
&self,
request: RepositoryAuthorizationRequest,
) -> Result<(), AuthorizationError> {
let repository = self
.repo_repo
.get(request.owner.as_ref(), request.repo.as_ref())
.await?
.ok_or_else(|| {
AuthorizationError::NotFound(format!(
"Repository not found: {}",
request.get_repo_path(),
))
})?;
if repository.is_public() {
return Ok(());
}
if request.user_id.is_none() {
return Err(AuthorizationError::Unauthorized);
}
let user_id = request.user_id.unwrap();
if repository.is_owned_by_user() {
if repository.owner_id != user_id {
return Err(AuthorizationError::Unauthorized);
}
} else {
let is_member = self
.org_repo
.is_member(repository.owner_id, user_id)
.await?;
if !is_member {
return Err(AuthorizationError::Unauthorized);
}
}
Ok(())
}
async fn verify_authorized_for_organization(
&self,
request: OrganizationAuthorizationRequest,
) -> Result<(), AuthorizationError> {
let role = self
.org_repo
.get_member_role(request.org_name.as_ref(), request.user_id)
.await?;
match role {
Some(OrganizationRole::Admin) => Ok(()),
_ => Err(AuthorizationError::Unauthorized),
}
}
async fn verify_authorized_for_question(
&self,
request: QuestionAuthorizationRequest,
) -> Result<(), AuthorizationError> {
let author_id = self
.question_repo
.get_question_author_id(
request.owner.as_ref(),
request.repo.as_ref(),
request.number,
)
.await?
.ok_or_else(|| {
AuthorizationError::NotFound(format!(
"Question not found: {}",
request.get_repo_path(),
))
})?;
if author_id != request.user_id {
return Err(AuthorizationError::Unauthorized);
}
Ok(())
}
async fn verify_authorized_for_answer(
&self,
request: AnswerAuthorizationRequest,
) -> Result<(), AuthorizationError> {
let author_id = self
.question_repo
.get_answer_author_id(request.answer_id)
.await?
.ok_or_else(|| {
AuthorizationError::NotFound(format!("Answer not found: {}", request.answer_id))
})?;
if author_id != request.user_id {
return Err(AuthorizationError::Unauthorized);
}
Ok(())
}
async fn verify_authorized_for_comment(
&self,
request: CommentAuthorizationRequest,
) -> Result<(), AuthorizationError> {
let author_id = self
.question_repo
.get_comment_author_id(request.comment_id)
.await?
.ok_or_else(|| {
AuthorizationError::NotFound(format!("Comment not found: {}", request.comment_id))
})?;
if author_id != request.user_id {
return Err(AuthorizationError::Unauthorized);
}
Ok(())
}
}
stored oauth token and re-enabled git auth
mikkel•ac5c6b67d ago
new 404 error in authorization service and NotFoudn
baepaul•432dc8e9d ago
temporarily removing auth on git push for testing
baepaul•520964210d ago
moved auth logic into extractor
mike•c7f51ff12d ago
added auth for repo creation
mike•cf3beb612d ago
added authorization for git http endpoints
mike•17e95b612d ago
optimizes a few methods by combining db round trips
mike•3158a9213d ago
added add member to org api
mike•47e08a813d ago
implemented voting system
mike•cfbefbc13d ago
rolled back to standard question apis
mike•ef0cc8213d ago
added auth for update apis
mike•910376914d ago
updated repo authorization to take either repo id or owner/repo pair
mike•406d89814d ago
put repo apis behind authorization
mike•3736fbc15d ago
created authorization service
mike•b104ec915d ago