Fix editing project team member permissions (#5315)

* Fix editing project team member permissions

* prepare

* add success notifications

---------

Co-authored-by: tdgao <mr.trumgao@gmail.com>
This commit is contained in:
aecsocket
2026-02-06 22:56:36 +00:00
committed by GitHub
parent d713cea180
commit b34564a770
2 changed files with 48 additions and 16 deletions

View File

@@ -499,9 +499,26 @@
</div>
</template>
<div class="input-group">
<!--
if we save changes and update an org member which:
- is not currently overridden (!allOrgMembers[index].oldOverride)
- and we're not changing them to be overridden (!allOrgMembers[index].override)
then we end up editing an org member which, in the backend, doesn't exist.
the api doesn't let us do that, we can only do:
- !override -> override: POST member
- override -> !override: DELETE member
- override -> override: PATCH member
- !override -> !override: do nothing
we don't allow clicking the button in that last case.
-->
<button
class="iconified-button brand-button"
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER"
:disabled="
(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
(!allOrgMembers[index].oldOverride && !allOrgMembers[index].override)
"
@click="updateOrgMember(index)"
>
<SaveIcon />
@@ -565,22 +582,21 @@ function initMembers() {
const selectedMembersForOrg = orgMembers.map((partialOrgMember) => {
const foundMember = allMembers.value.find((tM) => tM.user.id === partialOrgMember.user.id)
const returnVal = foundMember ?? partialOrgMember
// If replacing a partial with a full member, we need to mark as such.
returnVal.override = !!foundMember
returnVal.oldOverride = !!foundMember
returnVal.is_owner = partialOrgMember.is_owner
return returnVal
const base = foundMember ?? partialOrgMember
return {
...base,
override: !!foundMember,
oldOverride: !!foundMember,
is_owner: partialOrgMember.is_owner,
}
})
allOrgMembers.value = selectedMembersForOrg
allTeamMembers.value = allMembers.value.filter(
(x) => !selectedMembersForOrg.some((y) => y.user.id === x.user.id),
)
allTeamMembers.value = allMembers.value
.filter((x) => !selectedMembersForOrg.some((y) => y.user.id === x.user.id))
.map((x) => ({ ...x }))
}
watch([allMembers, organization, project, currentMember], initMembers)
@@ -688,6 +704,11 @@ const removeTeamMember = async (index) => {
},
)
await updateMembers()
addNotification({
title: 'Member removed',
text: "Your project's member has been removed.",
type: 'success',
})
} catch (err) {
addNotification({
title: 'An error occurred',
@@ -748,6 +769,11 @@ const transferOwnership = async (index) => {
user_id: allTeamMembers.value[index].user.id,
},
})
addNotification({
title: 'Member ownership transferred',
text: `${allTeamMembers.value[index].user.username} is now the owner of the project.`,
type: 'success',
})
await updateMembers()
} catch (err) {
addNotification({
@@ -795,6 +821,11 @@ async function updateOrgMember(index) {
)
}
await updateMembers()
addNotification({
title: 'Member(s) updated',
text: "Your project's member(s) has been updated.",
type: 'success',
})
} catch (err) {
addNotification({
title: 'An error occurred',

View File

@@ -14,6 +14,7 @@ use crate::queue::session::AuthQueue;
use crate::routes::ApiError;
use actix_web::{HttpRequest, HttpResponse, web};
use ariadne::ids::UserId;
use eyre::eyre;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
@@ -708,10 +709,10 @@ pub async fn edit_team_member(
DBTeamMember::get_from_user_id_pending(id, user_id, &**pool)
.await?
.ok_or_else(|| {
ApiError::CustomAuthentication(
"You don't have permission to edit members of this team"
.to_string(),
)
ApiError::Request(eyre!(
"This member does not exist in this team - \
the member must first be created via `POST`"
))
})?;
let mut transaction = pool.begin().await?;