-
Notifications
You must be signed in to change notification settings - Fork 1k
Manage organization "People" #11342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Manage organization "People" #11342
Conversation
divbzero
commented
May 5, 2022
- Closes Add Members to an Organization account #11076.
- Closes Remove Members from an Organization account #11077.
- Closes Cancel User invitation from an Organization account #11078.
- Closes Assign role to an Organization account Member #11081.
- Closes Accept/reject Organization invite #11083.
Borrowing styles from list of projects for now.
Use the same gradient of grays as white-cube.svg.
- {.package-snippet__project-badge => .package-snippet__badge} - {.package-snippet__released => .package-snippet__created}
Cards with similar styling but different icons: - .organization-snippet - .package-snippet
"Manage" link for each organization listed on /manage/organizations/ navigates to a "Collaborators" management page for the organization.
Discussed with @s-mm and renaming to differentiate "people" in organizations from "collaborators" in projects.
I already added "Your organizations" to the sidebar for desktop but forgot to add it for mobile.
Status badges: - "Request Submitted" if still awaiting approve/decline - "Inactive" if declined or inactive for other reasons Role badges: - "Manager" for organization manager - "Owner" for organization owner - "Billing Manager" for billing manager
This is my third attempt at stabilizing these tests but I finally see the light of what I was doing wrong. I should not rely on probabilistic tests at all even if the probability is "very very very low". @sterbo actually suggested this earlier but it didn't fully click in my head until now. Co-authored-by: sterbo <[email protected]>
- Remove "Billing Manager" choice for non-companies - Add "Billing Manager" role description from @s-mm
- Copy-paste `revoke_project_role_invitation(...)`.
- Implement pypi#11078.
- Copy-paste `TestVerifyProjectRole`.
- Implement approve/decline tests.
Notification emails for the organization owner: - organization-member-invited - organization-member-invite-canceled - organization-member-invite-declined - organization-member-added - organization-member-removed - organization-member-role-changed Notification emails for the invited user: - verify-organization-role - canceled-as-invited-organization-member - declined-as-invited-organization-member - added-as-organization-member - removed-as-organization-member - role-changed-as-organization-member
b90fb95
to
a332623
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warehouse/organizations/models.py
Outdated
# acls = [ | ||
# (Allow, "group:admins", "manage:organization"), | ||
# (Allow, "group:moderators", "manage:team"), | ||
# ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ewdurbin Do we want to grant PyPI admins or moderators sudo privileges to manage organizations or teams?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. But as is we currently perform all admin actions via the admin app/interface.
Granting permissions this way would allow admins to use the user-facing interface to make these changes, I assume? That'd be a departure.... @di thoughts here? My preference is probably to maintain the current separation of concerns via the utility sets a la warehouse/utils/project.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been thinking about doing this vs. maintaining a very similar but separate set of views. I think it's probably better to do the latter, because we can add additional context, and also easily do things like not send emails/notifications/events when admins perform actions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. In a quick review it appears that we do the ACL dance on Projects to set the level of access that PyPI Admins/Moderators have. So we should do similar here, but the use-case won't be a "sudo" mode, just setting the ACLs so we can act on them in warehouse/admin views.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warehouse/organizations/models.py
Outdated
elif role.role_name == OrganizationRoleType.Manager: | ||
acls.append((Allow, f"user:{role.user.id}", ["manage:team"])) | ||
else: # TODO Member: Do we need this? May be covered by Project acl? | ||
acls.append((Allow, f"user:{role.user.id}", ["organization:member"])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ewdurbin There are two things we want to confirm here:
- We think that project-related permissions will be governed on the project level with
"manage:project"
and we don’t need anything for that here. Would you agree? - Will users need to be logged in and an organization member to view the organization’s projects and people? If yes, we think we’ll want to define a
"view:organization"
permission.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ewdurbin We’ve added "view:organization"
with commit 5e453dc so that:
- All organization members will have view access to organization settings
- Non-owners can remove themselves from the organization
This feels like a sensible approach, but let us know your thoughts and we can adjust as needed.
(In the future, probably when package namespacing is implemented, it could make sense to add a public view of each organization’s packages at https://pypi.org/~pypa/
or https://pypi.org/@pypa/
.)
- Allow read access to organization for all members - Allow non-owners to remove self from organization
ba4bfcf
to
5e453dc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No blockers. Could another of @pypa/warehouse-committers review? One place I didn't have a lot of time to look over was the email templates, as I hope these will be re-considered during user testing.
submitter_user = user_service.get_user(data.get("submitter_id")) | ||
organization.record_event( | ||
tag="organization:organization_role:declined", | ||
ip_address=request.remote_addr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this instance we may want to either anonymize the ip_address on the event for the OrganizationEvent by hard coding to 0.0.0.0/127.0.0.1 or somehow be sure we don't leak it on display to Organization admins.
This is a potential vector for abuse/leak. I can just invite a bunch of people to an org and get their IPs if they decline (or accept I suppose!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would probably want to anonymize/redact IP addresses on project collaboration invitations as well.
A pattern we use elsewhere is adding "redact_ip": True
to "account:email:sent"
events:
https://github.com/pypa/warehouse/blob/64fc7f1e4922017b476643ba7eda0859d69ae137/warehouse/admin/views/emails.py#L102-L111
And then using that flag to mask the IP address when displaying the event to users:
https://github.com/pypa/warehouse/blob/64fc7f1e4922017b476643ba7eda0859d69ae137/warehouse/templates/manage/account.html#L669-L672
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A somewhat related issue regarding project deletion/re-use:
warehouse/organizations/models.py
Outdated
# acls = [ | ||
# (Allow, "group:admins", "manage:organization"), | ||
# (Allow, "group:moderators", "manage:team"), | ||
# ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. In a quick review it appears that we do the ACL dance on Projects to set the level of access that PyPI Admins/Moderators have. So we should do similar here, but the use-case won't be a "sudo" mode, just setting the ACLs so we can act on them in warehouse/admin views.
@@ -1028,6 +1039,20 @@ def user_organizations(request): | |||
} | |||
|
|||
|
|||
def organization_owners(request, organization): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason I want this to exist on the Organization service... But we do a similar thing for Project roles. I can't recall if there's a reason for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to do this, it could be a bigger refactor where we move the helper functions for Project roles to warehouse.packaging.services
.
@@ -14,4 +14,4 @@ | |||
|
|||
{% extends "email/_base/subject.txt" %} | |||
|
|||
{% block subject %}{{ initiator_username }} has invited you to join the {{ project_name }} project{% endblock %} | |||
{% block subject %}You have joined the {{ project_name }} project{% endblock %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
@@ -19,7 +19,7 @@ | |||
|
|||
{% block main %} | |||
{% if project_invites %} | |||
<h1 class="page-title">{% trans project_count=project_invites|length %}Pending invitations ({{ project_count }}){% endtrans %}</h1> | |||
<h1 class="page-title">{% trans invitation_count=project_invites|length %}Pending invitations ({{ invitation_count }}){% endtrans %}</h1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another good catch 🐟
Update error text based on @ewdurbin's suggestion in pypi#11342.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor wording stuff.
warehouse/accounts/views.py
Outdated
token = request.params.get("token") | ||
data = token_service.loads(token) | ||
except TokenExpired: | ||
return _error(request._("Expired token: request a new organization invite")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double-checking: should this (and the "invalid token" message) say "invite" or "invitation"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated error text in 2480614. We seem to use ‘invitation’ everywhere else and it is unambiguously a noun.
warehouse/locale/messages.pot
Outdated
msgid "Email address ${email_address} verified. ${confirm_message}." | ||
msgstr "" | ||
|
||
#: warehouse/accounts/views.py:839 | ||
#: warehouse/accounts/views.py:846 | ||
msgid "Expired token: request a new organization invite" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Same question - whether "invite" or "invitation" is right here, and in the Invalid token message below as well.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Updated error text in 2480614.)
#: warehouse/templates/manage/organizations.html:22 | ||
#: warehouse/templates/manage/projects.html:22 | ||
#, python-format | ||
msgid "Pending invitations (%(invitation_count)s)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will read better as
"(%(invitation_count)s) pending invitations"
or as
"Pending invitations: (%(invitation_count)s)" [note the colon]
@@ -18,8 +18,35 @@ | |||
{% block title %}{% trans %}Your organizations{% endtrans %}{% endblock %} | |||
|
|||
{% block main %} | |||
<h1 class="page-title">{% trans organization_count=organizations|length %}Your organizations ({{ organization_count }}){% endtrans %}</h1> | |||
{% if organization_invites %} | |||
<h1 class="page-title">{% trans invitation_count=organization_invites|length %}Pending invitations ({{ invitation_count }}){% endtrans %}</h1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<h1 class="page-title">{% trans invitation_count=organization_invites|length %}Pending invitations ({{ invitation_count }}){% endtrans %}</h1> | |
<h1 class="page-title">{% trans invitation_count=organization_invites|length %}Pending invitations: ({{ invitation_count }}){% endtrans %}</h1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could affect other heading text as well such as Your projects (5) or Your organizations (2) which appear in the side pane and the main pane on different pages.
Update error text based on @brainwane's suggestion in pypi#11342.