mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Access control: Allow users with permission to update team, dashboard and folder permissions to list users in OSS (#48275)
* Remove banner when missing permissions to list users * For OSS allow users to list other users if they have permissions to write either team, dashboard or folder permissions
This commit is contained in:
@@ -249,7 +249,19 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
|
||||
// current org without requirement of user to be org admin
|
||||
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
|
||||
orgRoute.Get("/users/lookup", authorize(reqOrgAdminFolderAdminOrTeamAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.GetOrgUsersForCurrentOrgLookup))
|
||||
lookupEvaluator := func() ac.Evaluator {
|
||||
if hs.Cfg.IsEnterprise {
|
||||
return ac.EvalPermission(ac.ActionOrgUsersRead)
|
||||
}
|
||||
// For oss we allow users with access to update permissions on either folders, teams or dashboards to perform the lookup
|
||||
return ac.EvalAny(
|
||||
ac.EvalPermission(ac.ActionOrgUsersRead),
|
||||
ac.EvalPermission(ac.ActionTeamsPermissionsWrite),
|
||||
ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite),
|
||||
ac.EvalPermission(dashboards.ActionFoldersPermissionsWrite),
|
||||
)
|
||||
}
|
||||
orgRoute.Get("/users/lookup", authorize(reqOrgAdminFolderAdminOrTeamAdmin, lookupEvaluator()), routing.Wrap(hs.GetOrgUsersForCurrentOrgLookup))
|
||||
})
|
||||
|
||||
// create new org
|
||||
|
||||
@@ -109,7 +109,7 @@ func (ss *SQLStore) GetOrgUsers(ctx context.Context, query *models.GetOrgUsersQu
|
||||
whereConditions = append(whereConditions, fmt.Sprintf("%s.is_service_account = ?", ss.Dialect.Quote("user")))
|
||||
whereParams = append(whereParams, ss.Dialect.BooleanStr(false))
|
||||
|
||||
if !accesscontrol.IsDisabled(ss.Cfg) && query.User != nil {
|
||||
if ss.Cfg.IsEnterprise && !accesscontrol.IsDisabled(ss.Cfg) && query.User != nil {
|
||||
acFilter, err := accesscontrol.Filter(query.User, "org_user.user_id", "users:id:", accesscontrol.ActionOrgUsersRead)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -62,6 +62,10 @@ func TestSQLStore_GetOrgUsers(t *testing.T) {
|
||||
}
|
||||
|
||||
store := InitTestDB(t, InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagAccesscontrol}})
|
||||
store.Cfg.IsEnterprise = true
|
||||
defer func() {
|
||||
store.Cfg.IsEnterprise = false
|
||||
}()
|
||||
seedOrgUsers(t, store, 10)
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { Alert, Button, Form, HorizontalGroup, Input, Select } from '@grafana/ui';
|
||||
import { Button, Form, HorizontalGroup, Select } from '@grafana/ui';
|
||||
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||
import { TeamPicker } from 'app/core/components/Select/TeamPicker';
|
||||
import { UserPicker } from 'app/core/components/Select/UserPicker';
|
||||
@@ -12,20 +12,12 @@ export interface Props {
|
||||
title?: string;
|
||||
permissions: string[];
|
||||
assignments: Assignments;
|
||||
canListUsers: boolean;
|
||||
onCancel: () => void;
|
||||
onAdd: (state: SetPermission) => void;
|
||||
}
|
||||
|
||||
export const AddPermission = ({
|
||||
title = 'Add Permission For',
|
||||
permissions,
|
||||
assignments,
|
||||
canListUsers,
|
||||
onAdd,
|
||||
onCancel,
|
||||
}: Props) => {
|
||||
const [target, setPermissionTarget] = useState<PermissionTarget>(PermissionTarget.User);
|
||||
export const AddPermission = ({ title = 'Add Permission For', permissions, assignments, onAdd, onCancel }: Props) => {
|
||||
const [target, setPermissionTarget] = useState<PermissionTarget>(PermissionTarget.None);
|
||||
const [teamId, setTeamId] = useState(0);
|
||||
const [userId, setUserId] = useState(0);
|
||||
const [builtInRole, setBuiltinRole] = useState('');
|
||||
@@ -33,8 +25,8 @@ export const AddPermission = ({
|
||||
|
||||
const targetOptions = useMemo(() => {
|
||||
const options = [];
|
||||
if (assignments.users && canListUsers) {
|
||||
options.push({ value: PermissionTarget.User, label: 'User', isDisabled: false });
|
||||
if (assignments.users) {
|
||||
options.push({ value: PermissionTarget.User, label: 'User' });
|
||||
}
|
||||
if (assignments.teams) {
|
||||
options.push({ value: PermissionTarget.Team, label: 'Team' });
|
||||
@@ -43,7 +35,7 @@ export const AddPermission = ({
|
||||
options.push({ value: PermissionTarget.BuiltInRole, label: 'Role' });
|
||||
}
|
||||
return options;
|
||||
}, [assignments, canListUsers]);
|
||||
}, [assignments]);
|
||||
|
||||
useEffect(() => {
|
||||
if (permissions.length > 0) {
|
||||
@@ -56,22 +48,11 @@ export const AddPermission = ({
|
||||
(target === PermissionTarget.User && userId > 0) ||
|
||||
(PermissionTarget.BuiltInRole && OrgRole.hasOwnProperty(builtInRole));
|
||||
|
||||
const renderMissingListUserRights = () => {
|
||||
return (
|
||||
<Alert severity="info" title="Missing permission">
|
||||
You are missing the permission to list users (org.users:read). Please contact your administrator to get this
|
||||
resolved.
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="cta-form" aria-label="Permissions slider">
|
||||
<CloseButton onClick={onCancel} />
|
||||
<h5>{title}</h5>
|
||||
|
||||
{target === PermissionTarget.User && !canListUsers && renderMissingListUserRights()}
|
||||
|
||||
<Form
|
||||
name="addPermission"
|
||||
maxWidth="none"
|
||||
@@ -87,10 +68,9 @@ export const AddPermission = ({
|
||||
disabled={targetOptions.length === 0}
|
||||
/>
|
||||
|
||||
{target === PermissionTarget.User && canListUsers && (
|
||||
{target === PermissionTarget.User && (
|
||||
<UserPicker onSelected={(u) => setUserId(u.value || 0)} className={'width-20'} />
|
||||
)}
|
||||
{target === PermissionTarget.User && !canListUsers && <Input disabled={true} className={'width-20'} />}
|
||||
|
||||
{target === PermissionTarget.Team && (
|
||||
<TeamPicker onSelected={(t) => setTeamId(t.value?.id || 0)} className={'width-20'} />
|
||||
|
||||
@@ -29,8 +29,6 @@ export type Props = {
|
||||
addPermissionTitle?: string;
|
||||
resource: string;
|
||||
resourceId: ResourceId;
|
||||
|
||||
canListUsers: boolean;
|
||||
canSetPermissions: boolean;
|
||||
};
|
||||
|
||||
@@ -39,7 +37,6 @@ export const Permissions = ({
|
||||
buttonLabel = 'Add a permission',
|
||||
resource,
|
||||
resourceId,
|
||||
canListUsers,
|
||||
canSetPermissions,
|
||||
addPermissionTitle,
|
||||
}: Props) => {
|
||||
@@ -145,7 +142,6 @@ export const Permissions = ({
|
||||
onAdd={onAdd}
|
||||
permissions={desc.permissions}
|
||||
assignments={desc.assignments}
|
||||
canListUsers={canListUsers}
|
||||
onCancel={() => setIsAdding(false)}
|
||||
/>
|
||||
</SlideDown>
|
||||
|
||||
@@ -22,6 +22,7 @@ export type SetPermission = {
|
||||
};
|
||||
|
||||
export enum PermissionTarget {
|
||||
None = 'None',
|
||||
Team = 'Team',
|
||||
User = 'User',
|
||||
BuiltInRole = 'builtInRole',
|
||||
|
||||
@@ -11,15 +11,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const AccessControlDashboardPermissions = ({ dashboard }: Props) => {
|
||||
const canListUsers = contextSrv.hasPermission(AccessControlAction.OrgUsersRead);
|
||||
const canSetPermissions = contextSrv.hasPermission(AccessControlAction.DashboardsPermissionsWrite);
|
||||
|
||||
return (
|
||||
<Permissions
|
||||
resource={'dashboards'}
|
||||
resourceId={dashboard.uid}
|
||||
canListUsers={canListUsers}
|
||||
canSetPermissions={canSetPermissions}
|
||||
/>
|
||||
);
|
||||
return <Permissions resource={'dashboards'} resourceId={dashboard.uid} canSetPermissions={canSetPermissions} />;
|
||||
};
|
||||
|
||||
@@ -33,18 +33,12 @@ export const AccessControlFolderPermissions = ({ uid, getFolderByUid, navModel }
|
||||
getFolderByUid(uid);
|
||||
}, [getFolderByUid, uid]);
|
||||
|
||||
const canListUsers = contextSrv.hasPermission(AccessControlAction.OrgUsersRead);
|
||||
const canSetPermissions = contextSrv.hasPermission(AccessControlAction.FoldersPermissionsWrite);
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents>
|
||||
<Permissions
|
||||
resource="folders"
|
||||
resourceId={uid}
|
||||
canListUsers={canListUsers}
|
||||
canSetPermissions={canSetPermissions}
|
||||
/>
|
||||
<Permissions resource="folders" resourceId={uid} canSetPermissions={canSetPermissions} />
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,6 @@ type TeamPermissionsProps = {
|
||||
|
||||
// TeamPermissions component replaces TeamMembers component when the accesscontrol feature flag is set
|
||||
const TeamPermissions = (props: TeamPermissionsProps) => {
|
||||
const canListUsers = contextSrv.hasPermission(AccessControlAction.OrgUsersRead);
|
||||
const canSetPermissions = contextSrv.hasPermissionInMetadata(
|
||||
AccessControlAction.ActionTeamsPermissionsWrite,
|
||||
props.team
|
||||
@@ -24,7 +23,6 @@ const TeamPermissions = (props: TeamPermissionsProps) => {
|
||||
buttonLabel="Add member"
|
||||
resource="teams"
|
||||
resourceId={props.team.id}
|
||||
canListUsers={canListUsers}
|
||||
canSetPermissions={canSetPermissions}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user