Repository navigation
Conversation
A membership now has a role, through a Role model and an explicit through model on the existing membership table. Only an admin or staff can edit or delete an organization or manage its members and their roles, and the last admin can't be removed or demoted. Any member keeps what they have on the organization's repositories, private ones included, and can still leave. Every existing member becomes an admin, so existing organizations behave the same after the migration. An organization's creator is an admin from now on. The members list gains each member's role, the organization detail gains the requester's role, and PUT on a member takes an optional role. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaZ4qUSJnKeDYRubKa6DYK
… a hard delete keeps the last admin A request that waited for the org's lock now uses the rights its sender has once it gets the lock, so an admin being demoted can't promote themselves back. A user hard delete is refused while they're the last active admin of an org. Deactivating stays possible, as it's how staff block an account. A membership row inserted without a role is an admin, which is what code older than the column means by a member. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaZ4qUSJnKeDYRubKa6DYK
… unused related names Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaZ4qUSJnKeDYRubKa6DYK
snyaggarwal
requested changes
Nov 6, 2026
snyaggarwal
left a comment
Contributor
There was a problem hiding this comment.
Other thoughts:
- Do we want role to be in facet as well?
- Should member roles be part of org summary?
| from core.users.models import Follow | ||
|
|
||
|
|
||
| class Role(models.Model): |
Contributor
There was a problem hiding this comment.
model name and table name dont match intent.
Generic: (I would recommend)
Model name feels generic and shouldn't be here, it should be in core.common.models
and tablename needs to be roles -- db_table = 'roles'
| class Meta: | ||
| db_table = 'org_roles' | ||
|
|
||
| name = models.CharField(max_length=100, unique=True) |
Contributor
There was a problem hiding this comment.
this needs to be restricted with choices
| return ClientConfigSerializer(obj.client_configs.filter( | ||
| is_active=True), many=True).data if self.include_client_configs else None | ||
|
|
||
| def get_user_role(self, obj): |
Contributor
There was a problem hiding this comment.
user_role should be popped from response for anonymous requests
|
|
||
| def is_last_admin(self, user_profile): | ||
| """True if no other active admin would remain without `user_profile`. Every org keeps at least one.""" | ||
| return self.is_admin(user_profile) and not self.memberships.filter( |
Contributor
There was a problem hiding this comment.
this is making to queries to memberships relation.
admins = self.memberships.filter(role_id=ROLE_ADMIN_ID, userprofile__is_active=True).values_list('userprofile_id', flat=True)
return len(admins) == 1 and admins[0] == user_profile.id
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Today every member of an organization has the same rights over it. This gives each membership a role, so editing or deleting the org and managing its members need an admin. It's part of OpenConceptLab/ocl_online#648. The TBv3 side comes in a separate PR.
What changes
Model
Role(tableorg_roles), with rowsadminandmemberincore/fixtures/org_roles.yaml.OrganizationMember, an explicitthroughonUserProfile.organizations, on the existing tableuser_profiles_organizations, addingrole.Needs an admin or staff
Stays with any member
is_admin_forkeeps its meaning, since it's about a repository, not the org's role.The last active admin can't be removed, leave or be demoted, and staff are held to this too (409). A user hard delete is refused the same way, naming the orgs. Deactivating a user is still possible, and staff can promote someone afterwards.
API
GET /orgs/:org/members/and/users/: each row hasrole, in the list, verbose and summary shapes.GET /orgs/:org/: addsuser_role, the requester's role or null.PUT /orgs/:org/members/:user/: optional body{"role": "admin"|"member"}. A new member defaults to member, and an existing one keeps their role if none is sent, so clients that send no body keep working. A role that doesn't exist is 400, demoting the last admin is 409. A repeat PUT no longer records a secondJoinedevent.DELETE /orgs/:org/members/:user/: removing the last admin is 409, beside the existing 409 for the only member.One thing that isn't obvious
Editing a repository looks up its parent org by running
OrganizationDetailViewwith the same PUT request, so that view's write permission applies to repository edits too.get_object_for_pathnow passesis_parent_lookup=True, and the org view keeps the member check in that case. Without it, members couldn't edit repositories.Migration and rollout
orgs 0023creates and seedsRole, and addsrole_idwith oneALTER TABLEat database default admin, so every existing member becomes an admin and existing orgs behave the same.users 0038changes state only.Rehearsed on a scratch database with old code on the new schema, forward, reverse and forward again.
Two things to confirm in review (OpenConceptLab/ocl_online#648): that every existing member becomes an admin, and that deactivating an org's last active admin stays allowed.
Tests
pylint -j0 core/: 10.00/10.Reviews
Two Codex passes, a simplification review and an adversarial pass, in full on the ticket. Fixed from them: a hard delete could remove the last admin, older code could create an org with no admin during a rollout, and rights were read before taking the org's lock. About 280 lines of repeated tests were cut. No P0 or P1 is open.
🤖 Generated with Claude Code
https://claude.ai/code/session_01AaZ4qUSJnKeDYRubKa6DYK