Skip to content
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

fix: Reading role permissions generates 500 errors #3009

Merged
merged 30 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
67ac16f
fix: Reading role permissions generates 500 errors
novakzaballa Nov 20, 2023
38c076d
Add todo
novakzaballa Nov 21, 2023
61aff35
Add issue
novakzaballa Nov 21, 2023
cc95b3f
fix assign roles in user permissions
novakzaballa Nov 22, 2023
487283f
Add roles to user serializer
novakzaballa Nov 28, 2023
dc0dc4d
UPdate editpermission for user roles
novakzaballa Nov 28, 2023
85322bf
delete logs and update users
novakzaballa Nov 28, 2023
f52fb32
Update style for the roles in CreateRole Modal
novakzaballa Nov 29, 2023
4a071a8
Fix bug when try open editPermission modal
novakzaballa Nov 30, 2023
e9c3e70
Fix bug when try open editPermission modal
novakzaballa Nov 30, 2023
e386f40
Merge branch 'main' into fix/reading-role-permissions-generates-500-e…
novakzaballa Nov 30, 2023
8787d43
Solve merge errors
novakzaballa Nov 30, 2023
cbd44cf
fix: Reading role permissions generates 500 error backend
novakzaballa Nov 30, 2023
abeb1d1
Add exception in get_user_roles
novakzaballa Nov 30, 2023
0aa8d4e
update user when role is deleted
novakzaballa Dec 1, 2023
672b280
Merge branch 'fix/reading-role-permissions-generates-500-error-be' in…
novakzaballa Dec 1, 2023
4d17bf3
Add new url for user roles
novakzaballa Dec 1, 2023
8ab42d1
Use new RBAC endpoint for get user with their roles
novakzaballa Dec 1, 2023
92281ba
Add confitional chaining
novakzaballa Dec 1, 2023
5bb6235
revert force getOrganisation
novakzaballa Dec 5, 2023
3633b37
revert changes in backend
novakzaballa Dec 5, 2023
d5967f1
Add and implement delete user roles endpoint
novakzaballa Dec 7, 2023
95b9045
Add user-roles url
novakzaballa Dec 7, 2023
1dac921
Clean code
novakzaballa Dec 7, 2023
cfd05de
Merge branch 'main' into fix/reading-role-permissions-generates-500-e…
novakzaballa Dec 11, 2023
c08f9ee
Update roles groups
novakzaballa Dec 18, 2023
2805763
Update roles RTK
novakzaballa Dec 18, 2023
b54b420
Merge branch 'main' into fix/reading-role-permissions-generates-500-e…
novakzaballa Dec 22, 2023
4b98c2a
Solve error 500 in project, and environment settings page
novakzaballa Jan 8, 2024
cd8afba
Merge branch 'main' into fix/reading-role-permissions-generates-500-e…
novakzaballa Jan 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions api/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.conf import settings
from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ImproperlyConfigured
from django.core.mail import send_mail
from django.db import models
from django.db.models import Count, QuerySet
Expand Down Expand Up @@ -42,6 +43,9 @@
InviteLink,
)

if settings.IS_RBAC_INSTALLED:
from rbac.models import UserRole

logger = logging.getLogger(__name__)
mailer_lite = MailerLite()

Expand Down Expand Up @@ -243,6 +247,14 @@ def get_user_organisation(
% (self.id, getattr(organisation, "id", organisation))
)

def get_user_roles(self):
if not settings.IS_RBAC_INSTALLED:
raise ImproperlyConfigured(
"RBAC is not installed. Unable to retrieve user roles."
)

return UserRole.objects.filter(user=self)

def get_permitted_projects(
self, permission_key: str, tag_ids: typing.List[int] = None
) -> QuerySet[Project]:
Expand Down
12 changes: 11 additions & 1 deletion api/users/serializers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from django.conf import settings
from djoser.serializers import UserSerializer as DjoserUserSerializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from organisations.models import Organisation
from organisations.serializers import UserOrganisationSerializer

if settings.IS_RBAC_INSTALLED:
from rbac.serializers import UserRoleSerializer

from .models import FFAdminUser, UserPermissionGroup


Expand Down Expand Up @@ -56,9 +60,15 @@ class Meta:
class UserListSerializer(serializers.ModelSerializer):
role = serializers.SerializerMethodField(read_only=True)
join_date = serializers.SerializerMethodField(read_only=True)
if settings.IS_RBAC_INSTALLED:
roles = UserRoleSerializer(many=True, read_only=True, source="get_user_roles")

default_fields = ("id", "email", "first_name", "last_name", "last_login")
organisation_users_fields = ("role", "date_joined")
organisation_users_fields = (
"role",
"date_joined",
*([] if not settings.IS_RBAC_INSTALLED else ["roles"]),
)

class Meta:
model = FFAdminUser
Expand Down
3 changes: 2 additions & 1 deletion frontend/common/dispatcher/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,11 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), {
projectId,
})
},
getOrganisation(organisationId) {
getOrganisation(organisationId, force = false) {
Dispatcher.handleViewAction({
actionType: Actions.GET_ORGANISATION,
id: organisationId,
force,
})
},
getProject(projectId) {
Expand Down
6 changes: 3 additions & 3 deletions frontend/common/services/useRolePermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const rolePermissionService = service
Res['rolePermission'],
Req['getRolePermission']
>({
providesTags: (res) => [{ id: res?.id, type: 'RolePermission' }],
providesTags: (res) => [{ id: res?.id, type: 'rolePermission' }],
query: (query: Req['getRolePermission']) => ({
url: `organisations/${query.organisation_id}/roles/${query.role_id}/projects-permissions/?project=${query.project_id}`,
}),
Expand All @@ -56,7 +56,7 @@ export const rolePermissionService = service
Res['rolePermission'],
Req['getRolePermission']
>({
providesTags: (res) => [{ id: res?.id, type: 'RolePermission' }],
providesTags: (res) => [{ id: res?.id, type: 'rolePermission' }],
query: (query: Req['getRolePermission']) => ({
url: `organisations/${query.organisation_id}/roles/${query.role_id}/environments-permissions/?environment=${query.env_id}`,
}),
Expand All @@ -66,7 +66,7 @@ export const rolePermissionService = service
Res['rolePermission'],
Req['getRolePermission']
>({
providesTags: (res) => [{ id: res?.id, type: 'RolePermission' }],
providesTags: (res) => [{ id: res?.id, type: 'rolePermission' }],
query: (query: Req['getRolePermission']) => ({
url: `organisations/${query.organisation_id}/roles/${query.role_id}/projects-permissions/?project=${query.project_id}`,
}),
Expand Down
3 changes: 3 additions & 0 deletions frontend/common/services/useRolesUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export const rolesUserService = service
method: 'DELETE',
url: `organisations/${query.organisation_id}/roles/${query.role_id}/users/${query.user_id}/`,
}),
transformResponse: () => {
toast('User role was removed')
},
}),
getRolesPermissionUsers: builder.query<
Res['rolesUsers'],
Expand Down
6 changes: 5 additions & 1 deletion frontend/common/stores/organisation-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,11 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {

switch (action.actionType) {
case Actions.GET_ORGANISATION:
controller.getOrganisation(action.id || store.id, action.force)
controller.getOrganisation(
action.id,
action.force || store.id,
Copy link
Member

@kyle-ssg kyle-ssg Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the function here only has 2 arguments, so the last is the typo.

As for the following:

    action.force || store.id,

I've read this through along with the function itself and I don't think it makes sense. The point of the force parameter is to hit the API regardless of whether we already have data for that org. By adding ||store.id here, we essentially totally remove that functionality?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was reverted.

action.force,
)
break
case Actions.CREATE_PROJECT:
controller.createProject(action.name)
Expand Down
30 changes: 26 additions & 4 deletions frontend/web/components/EditPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {
FC,
useEffect,
useState,
useRef,
forwardRef,
useImperativeHandle,
} from 'react'
Expand Down Expand Up @@ -61,6 +62,7 @@ type EditPermissionModalType = {
level: PermissionLevel
name: string
onSave: () => void
onRemoveOrAddRole: () => void
parentId?: string
parentLevel?: string
parentSettingsLink?: string
Expand Down Expand Up @@ -102,6 +104,7 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
isGroup,
level,
name,
onRemoveOrAddRole,
onSave,
parentId,
parentLevel,
Expand Down Expand Up @@ -166,7 +169,7 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
{ data: usersData, isSuccess: userAdded },
] = useCreateRolesPermissionUsersMutation()

const [deleteRolePermissionUser] = useDeleteRolesPermissionUsersMutation()
const [deleteRolePermissionUser, {isSuccess: userDeleted}] = useDeleteRolesPermissionUsersMutation()

const [
createRolePermissionGroup,
Expand Down Expand Up @@ -241,6 +244,8 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
skip:
!id ||
envId ||
// TODO: https://github.com/Flagsmith/flagsmith/issues/3020
!role?.organisation ||
!Utils.getFlagsmithHasFeature('show_role_management'),
},
)
Expand All @@ -259,6 +264,17 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
!Utils.getFlagsmithHasFeature('show_role_management'),
},
)

useEffect(() => {
if (user) {
const resultArray = user.roles?.map((userRole) => ({
role: userRole.role,
user_role_id: userRole.id,
}))
setRolesSelected(resultArray)
}
}, [])

useEffect(() => {
if (
!organisationIsLoading &&
Expand Down Expand Up @@ -302,7 +318,7 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
(v) => v === `VIEW_${parentLevel.toUpperCase()}`,
)
) {
// e.g. trying to set an environment permission but don't have view_projec
// e.g. trying to set an environment permission but don't have view_project
setParentError(true)
} else {
setParentError(false)
Expand Down Expand Up @@ -471,11 +487,11 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
}
}
setRolesSelected((rolesSelected || []).filter((v) => v.role !== roleId))
toast('User role was removed')
}

useEffect(() => {
if (userAdded || groupAdded) {
onRemoveOrAddRole?.()
if (user) {
setRolesSelected(
(rolesSelected || []).concat({
Expand All @@ -496,6 +512,12 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
}
}, [userAdded, usersData, groupsData, groupAdded])

useEffect(() => {
if (userDeleted) {
onRemoveOrAddRole?.()
}
}, [userDeleted])

const getRoles = (roles = [], selectedRoles) => {
return roles
.filter((v) => selectedRoles.find((a) => a.role === v.id))
Expand Down Expand Up @@ -684,7 +706,7 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = forwardRef(
<MyRoleSelect
orgId={id}
level={level}
value={rolesSelected.map((v) => v.role)}
value={rolesSelected?.map((v) => v.role)}
onAdd={addRole}
onRemove={removeOwner}
isOpen={showRoles}
Expand Down
4 changes: 3 additions & 1 deletion frontend/web/components/base/forms/Tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ const Tabs = class extends React.Component {
return (
<div
key={`content${i}`}
className={`tab-item ${isSelected ? ' tab-active' : ''}`}
className={`tab-item ${isSelected ? ' tab-active' : ''} ${
this.props.isRoles && 'p-0'
}`}
>
{child}
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/web/components/modals/CreateRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ const CreateRole: FC<CreateRoleType> = ({
value={userGroupTab}
onChange={setUserGroupTab}
theme='pill m-0'
isRoles={true}
>
<TabItem
tabLabel={<Row className='justify-content-center'>Users</Row>}
Expand Down
4 changes: 4 additions & 0 deletions frontend/web/components/pages/OrganisationSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Icon from 'components/Icon'
import PageTitle from 'components/PageTitle'
import { getStore } from 'common/store'
import { getRoles } from 'common/services/useRole'
import OrganisationStore from 'common/stores/organisation-store'

const widths = [450, 150, 100]
const rolesWidths = [250, 600, 100]
Expand Down Expand Up @@ -233,6 +234,9 @@ const OrganisationSettingsPage = class extends Component {
onSave={() => {
AppActions.getOrganisation(AccountStore.getOrganisation().id)
}}
onRemoveOrAddRole={()=>{
AppActions.getOrganisation(AccountStore.getOrganisation().id, true)
}}
level='organisation'
roles={roles}
user={user}
Expand Down