diff --git a/src/webui/gitutils.jl b/src/webui/gitutils.jl index 885686ae75..9e68c4b1ff 100644 --- a/src/webui/gitutils.jl +++ b/src/webui/gitutils.jl @@ -25,13 +25,31 @@ getrepo(::GitLabAPI, owner::AbstractString, name::AbstractString) = getrepo(::GitHubAPI, owner::AbstractString, name::AbstractString) = @gf get_repo(PROVIDERS["github"].client, owner, name) + +abstract type AuthResult end +struct AuthSuccess <: AuthResult end +struct AuthFailure <: AuthResult + reason::AbstractString +end + +function AuthResult(res::Union{Bool,Nothing}, reason_for_failure::AbstractString) + if res === nothing || !res + return AuthFailure(reason_for_failure) + else + return AuthSuccess() + end +end + +is_success(res::AuthSuccess) = true +is_success(res::AuthFailure) = false + # Check for a user's authorization to release a package. # The criteria is simply whether the user is a collaborator for user-owned repos, # or whether they're an organization member or collaborator for organization-owned repos. -isauthorized(u, repo) = false +isauthorized(u, repo) = AuthFailure("Unkown user type / repo type") function isauthorized(u::User{GitHub.User}, repo::GitHub.Repo) if !get(CONFIG, "allow_private", false) - repo.private && return false + return AuthResult(!repo.private, "Repo $(repo.name) is private") end if repo.private @@ -40,20 +58,21 @@ function isauthorized(u::User{GitHub.User}, repo::GitHub.Repo) forge = u.forge end - hasauth = if repo.organization === nothing - @gf is_collaborator(forge, repo.owner.login, repo.name, u.user.login) + if repo.organization === nothing + hasauth = @gf is_collaborator(forge, repo.owner.login, repo.name, u.user.login) + return AuthResult(hasauth, "User $(u.user.name) is not a collaborator on repo $(repo.name)") else # First check for organization membership, and fall back to collaborator status. ismember = @gf is_member(forge, repo.organization.login, u.user.login) - something(ismember, false) || + hasauth = something(ismember, false) || @gf is_collaborator(forge, repo.organization.login, repo.name, u.user.login) + return AuthResult(hasauth, "User $(u.user.name) is not part of the org $(repo.organization.login) and not a collaborator on repo $(repo.name)") end - return something(hasauth, false) end function isauthorized(u::User{GitLab.User}, repo::GitLab.Project) if !get(CONFIG, "allow_private", false) - repo.visibility == "private" && return false + return AuthResult(repo.visibility !== "private", "Project $(repo.name) is private") end if repo.visibility == "private" @@ -62,8 +81,9 @@ function isauthorized(u::User{GitLab.User}, repo::GitLab.Project) forge = u.forge end - hasauth = if repo.namespace.kind == "user" - @gf is_collaborator(forge, repo.owner.username, repo.name, u.user.id) + if repo.namespace.kind == "user" + hasauth = @gf is_collaborator(forge, repo.owner.username, repo.name, u.user.id) + return AuthResult(hasauth, "User $(u.user.name) is not a member of project $(repo.name)") else # Same as above: group membership then collaborator check. nspath = split(repo.namespace.full_path, "/") @@ -76,9 +96,8 @@ function isauthorized(u::User{GitLab.User}, repo::GitLab.Project) something(ismember, false) && break end end - ismember + return AuthResult(ismember, "Project $(repo.name) belongs to the group $(repo.namespace.full_path), and user $(u.user.name) is not a member of that group or its parent group(s)") end - return something(hasauth, false) end # Get the raw (Julia)Project.toml text from a repository. diff --git a/src/webui/routes/register.jl b/src/webui/routes/register.jl index f5a752796f..3cabe71ebb 100644 --- a/src/webui/routes/register.jl +++ b/src/webui/routes/register.jl @@ -30,7 +30,10 @@ function register(r::HTTP.Request) owner, name = splitrepo(package) repo = getrepo(u.forge, owner, name) repo === nothing && return json(400; error="Repository was not found") - isauthorized(u, repo) || return json(400; error="Unauthorized to release this package") + auth_result = isauthorized(u, repo) + if !is_success(auth_result) + return json(400; error="Unauthorized to release this package. Reason: $(auth_result.reason)") + end # Get the (Julia)Project.toml, and make sure it is valid. toml = gettoml(u.forge, repo, ref)