Allow to select users and groups for acl management
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
parent
e4a5747cda
commit
97bb847d75
|
@ -43,7 +43,7 @@ return ['routes' => [
|
|||
],
|
||||
[
|
||||
'name' => 'Folder#setManageACL',
|
||||
'url' => '/folders/{id}/groups/{group}/manageACL',
|
||||
'url' => '/folders/{id}/manageACL',
|
||||
'verb' => 'POST'
|
||||
],
|
||||
[
|
||||
|
|
49
js/Api.ts
49
js/Api.ts
|
@ -7,18 +7,31 @@ export interface Group {
|
|||
displayname: string;
|
||||
}
|
||||
|
||||
export interface GroupProps {
|
||||
permissions: number;
|
||||
manage_acl: boolean;
|
||||
export interface OCSUser {
|
||||
uid: string;
|
||||
displayname: string;
|
||||
}
|
||||
|
||||
export interface OCSGroup {
|
||||
gid: string;
|
||||
displayname: string;
|
||||
}
|
||||
|
||||
export interface ManageRuleProps {
|
||||
type: string;
|
||||
id: string;
|
||||
displayname: string;
|
||||
}
|
||||
|
||||
|
||||
export interface Folder {
|
||||
id: number;
|
||||
mount_point: string;
|
||||
quota: number;
|
||||
size: number;
|
||||
groups: { [group: string]: GroupProps };
|
||||
groups: { [group: string]: number };
|
||||
acl: boolean;
|
||||
manage: ManageRuleProps[];
|
||||
}
|
||||
|
||||
export class Api {
|
||||
|
@ -79,8 +92,10 @@ export class Api {
|
|||
});
|
||||
}
|
||||
|
||||
setManageACL(folderId: number, group: string, manageACL: boolean): Thenable<void> {
|
||||
return $.post(this.getUrl(`folders/${folderId}/groups/${group}/manageACL`), {
|
||||
setManageACL(folderId: number, type: string, id: string, manageACL: boolean): Thenable<void> {
|
||||
return $.post(this.getUrl(`folders/${folderId}/manageACL`), {
|
||||
mappingType: type,
|
||||
mappingId: id,
|
||||
manageAcl: manageACL ? 1 : 0
|
||||
});
|
||||
}
|
||||
|
@ -102,4 +117,26 @@ export class Api {
|
|||
mountpoint
|
||||
});
|
||||
}
|
||||
|
||||
aclMappingSearch(folderId: number, search: string): Thenable<{groups: OCSGroup[], users: OCSUser[]}> {
|
||||
return $.getJSON(this.getUrl(`folders/${folderId}/search?format=json&search=${search}`))
|
||||
.then((data: OCSResult<{ groups: OCSGroup[]; users: OCSUser[]; }>) => {
|
||||
return {
|
||||
groups: data.ocs.data.groups.map((item) => {
|
||||
return {
|
||||
type: 'group',
|
||||
id: item.gid,
|
||||
displayname: item.displayname
|
||||
}
|
||||
}),
|
||||
users: Object.values(data.ocs.data.users).map((item) => {
|
||||
return {
|
||||
type: 'user',
|
||||
id: item.uid,
|
||||
displayname: item.displayname
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
21
js/App.css
21
js/App.css
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
|
||||
tr {
|
||||
height: 32px;
|
||||
min-height: 62px;
|
||||
}
|
||||
|
||||
th {
|
||||
|
@ -58,8 +58,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.mountpoint {
|
||||
width: 15%;
|
||||
}
|
||||
&.groups {
|
||||
width: 400px;
|
||||
min-width: 300px;
|
||||
}
|
||||
&.quota {
|
||||
width: 180px;
|
||||
}
|
||||
&.acl {
|
||||
display: flex;
|
||||
& > div {
|
||||
min-width: 250px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
& > label {
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.remove {
|
||||
|
@ -94,7 +110,6 @@
|
|||
.icon {
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
//transition: opacity 0.5s;
|
||||
|
||||
&.icon-visible {
|
||||
opacity: 0.5;
|
||||
|
|
114
js/App.tsx
114
js/App.tsx
|
@ -1,14 +1,15 @@
|
|||
import * as React from 'react';
|
||||
import {ChangeEvent, Component} from 'react';
|
||||
|
||||
import {Api, Folder, Group} from './Api';
|
||||
import {Api, Folder, Group, ManageRuleProps, OCSGroup, OCSUser} from './Api';
|
||||
import {FolderGroups} from './FolderGroups';
|
||||
import {QuotaSelect} from './QuotaSelect';
|
||||
|
||||
import './App.css';
|
||||
import {SubmitInput} from "./SubmitInput";
|
||||
import {SortArrow} from "./SortArrow";
|
||||
import FlipMove from "react-flip-move";
|
||||
import AsyncSelect from 'react-select/async'
|
||||
import Thenable = JQuery.Thenable;
|
||||
|
||||
const defaultQuotaOptions = {
|
||||
'1 GB': 1073741274,
|
||||
|
@ -70,7 +71,8 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
quota: -3,
|
||||
size: 0,
|
||||
id,
|
||||
acl: false
|
||||
acl: false,
|
||||
manage: []
|
||||
});
|
||||
this.setState({folders});
|
||||
});
|
||||
|
@ -98,7 +100,7 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
|
||||
addGroup(folder: Folder, group: string) {
|
||||
const folders = this.state.folders;
|
||||
folder.groups[group] = {permissions: OC.PERMISSION_ALL, manage_acl: false};
|
||||
folder.groups[group] = OC.PERMISSION_ALL;
|
||||
this.setState({folders});
|
||||
this.api.addGroup(folder.id, group);
|
||||
}
|
||||
|
@ -112,16 +114,17 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
|
||||
setPermissions(folder: Folder, group: string, newPermissions: number) {
|
||||
const folders = this.state.folders;
|
||||
folder.groups[group].permissions = newPermissions;
|
||||
folder.groups[group] = newPermissions;
|
||||
this.setState({folders});
|
||||
this.api.setPermissions(folder.id, group, newPermissions);
|
||||
}
|
||||
|
||||
setManageACL(folder: Folder, group: string, manageACL: boolean) {
|
||||
const folders = this.state.folders;
|
||||
folder.groups[group].manage_acl = manageACL;
|
||||
this.setState({folders});
|
||||
this.api.setManageACL(folder.id, group, manageACL);
|
||||
setManageACL(folder: Folder, type: string, id: string, manageACL: boolean) {
|
||||
this.api.setManageACL(folder.id, type, id, manageACL);
|
||||
}
|
||||
|
||||
searchMappings(folder: Folder, search: string) {
|
||||
return this.api.aclMappingSearch(folder.id, search)
|
||||
}
|
||||
|
||||
setQuota(folder: Folder, quota: number) {
|
||||
|
@ -225,7 +228,6 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
onAddGroup={this.addGroup.bind(this, folder)}
|
||||
removeGroup={this.removeGroup.bind(this, folder)}
|
||||
onSetPermissions={this.setPermissions.bind(this, folder)}
|
||||
onSetManageACL={this.setManageACL.bind(this, folder)}
|
||||
/>
|
||||
</td>
|
||||
<td className="quota">
|
||||
|
@ -235,13 +237,21 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
onChange={this.setQuota.bind(this, folder)}/>
|
||||
</td>
|
||||
<td className="acl">
|
||||
<input type="checkbox" checked={folder.acl} disabled={!App.supportACL()}
|
||||
<input id={`acl-${folder.id}`} type="checkbox" className="checkbox" checked={folder.acl} disabled={!App.supportACL()}
|
||||
title={
|
||||
App.supportACL()?
|
||||
t('groupfolders', 'Advanced permissions allows setting permissions on a per-file basis but comes with a performance overhead'):
|
||||
t('groupfolders', 'Advanced permissions are only supported with Nextcloud 16 and up')}
|
||||
onChange={(event) => this.setAcl(folder, event.target.checked)}
|
||||
/>
|
||||
<label htmlFor={`acl-${folder.id}`}></label>
|
||||
{folder.acl &&
|
||||
<ManageAclSelect
|
||||
folder={folder}
|
||||
onChange={this.setManageACL.bind(this, folder)}
|
||||
onSearch={this.searchMappings.bind(this, folder)}
|
||||
/>
|
||||
}
|
||||
</td>
|
||||
<td className="remove">
|
||||
<a className="icon icon-delete icon-visible"
|
||||
|
@ -281,7 +291,7 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<FlipMove typeName='tbody'>
|
||||
<FlipMove typeName='tbody' enterAnimation="accordionVertical" leaveAnimation="accordionVertical">
|
||||
{rows}
|
||||
<tr>
|
||||
<td>
|
||||
|
@ -304,3 +314,81 @@ export class App extends Component<{}, AppState> implements OC.Plugin<OC.Search.
|
|||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface ManageAclSelectProps {
|
||||
folder: Folder;
|
||||
onChange: (type: string, id: string, manageAcl: boolean) => void;
|
||||
onSearch: (name: string) => Thenable<{ groups: OCSGroup[]; users: OCSUser[]; }>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
function ManageAclSelect({onChange, onSearch, folder}: ManageAclSelectProps) {
|
||||
const handleSearch = (inputValue: string) => {
|
||||
return new Promise(resolve => {
|
||||
onSearch(inputValue).then((result) => {
|
||||
resolve([...result.groups, ...result.users])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const typeLabel = (item) => {
|
||||
return item.type === 'user' ? 'User' : 'Group'
|
||||
}
|
||||
return <AsyncSelect
|
||||
loadOptions={handleSearch}
|
||||
isMulti
|
||||
cacheOptions
|
||||
defaultOptions
|
||||
defaultValue={folder.manage}
|
||||
isClearable={false}
|
||||
onChange={(option, details) => {
|
||||
if (details.action === 'select-option') {
|
||||
const addedOption = details.option
|
||||
onChange && onChange(addedOption.type, addedOption.id, true)
|
||||
}
|
||||
if (details.action === 'remove-value') {
|
||||
const removedValue = details.removedValue
|
||||
onChange && onChange(removedValue.type, removedValue.id, false)
|
||||
}
|
||||
}}
|
||||
placeholder={t('groupfolders', 'Users/groups that can manage')}
|
||||
getOptionLabel={(option) => `${option.displayname} (${typeLabel(option)})`}
|
||||
getOptionValue={(option) => option.type + '/' + option.id }
|
||||
styles={{
|
||||
control: base => ({
|
||||
...base,
|
||||
minHeight: 25,
|
||||
borderWidth: 1
|
||||
}),
|
||||
dropdownIndicator: base => ({
|
||||
...base,
|
||||
padding: 4
|
||||
}),
|
||||
clearIndicator: base => ({
|
||||
...base,
|
||||
padding: 4
|
||||
}),
|
||||
multiValue: base => ({
|
||||
...base,
|
||||
backgroundColor: 'var(--color-background-dark)',
|
||||
color: 'var(--color-text)'
|
||||
}),
|
||||
valueContainer: base => ({
|
||||
...base,
|
||||
padding: '0px 6px'
|
||||
}),
|
||||
input: base => ({
|
||||
...base,
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}),
|
||||
menu: (provided) => ({
|
||||
...provided,
|
||||
backgroundColor: 'var(--color-main-background)',
|
||||
borderColor: 'var(--color-border)',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
min-width: 300px;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background-color: var(--color-main-background);
|
||||
background-color: var(--color-main-background, #fff);
|
||||
z-index: 10;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid var(--color-border, #ddd);
|
||||
|
||||
td {
|
||||
padding: 2px 10px;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import './FolderGroups.css';
|
||||
import {SyntheticEvent} from "react";
|
||||
import {Folder, Group, GroupProps} from "./Api";
|
||||
import {Group} from "./Api";
|
||||
import Select from 'react-select'
|
||||
|
||||
function hasPermissions(value: number, check: number): boolean {
|
||||
|
@ -9,29 +9,24 @@ function hasPermissions(value: number, check: number): boolean {
|
|||
}
|
||||
|
||||
export interface FolderGroupsProps {
|
||||
groups: { [group: string]: GroupProps },
|
||||
groups: { [group: string]: number },
|
||||
allGroups?: Group[],
|
||||
onAddGroup: (name: string) => void;
|
||||
removeGroup: (name: string) => void;
|
||||
edit: boolean;
|
||||
showEdit: (event: SyntheticEvent<any>) => void;
|
||||
onSetPermissions: (name: string, permissions: number) => void;
|
||||
onSetManageACL: (name: string, manageACL: boolean) => void;
|
||||
}
|
||||
|
||||
export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, edit, showEdit, onSetPermissions, onSetManageACL}: FolderGroupsProps) {
|
||||
export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, edit, showEdit, onSetPermissions}: FolderGroupsProps) {
|
||||
if (edit) {
|
||||
const setPermissions = (change: number, groupId: string): void => {
|
||||
const newPermissions = groups[groupId].permissions ^ change;
|
||||
const newPermissions = groups[groupId] ^ change;
|
||||
onSetPermissions(groupId, newPermissions);
|
||||
};
|
||||
|
||||
const setManageACL = (change: boolean, groupId: string): void => {
|
||||
onSetManageACL(groupId, change);
|
||||
};
|
||||
|
||||
const rows = Object.keys(groups).map(groupId => {
|
||||
const group = groups[groupId];
|
||||
const permissions = groups[groupId];
|
||||
return <tr key={groupId}>
|
||||
<td>
|
||||
{(
|
||||
|
@ -46,22 +41,17 @@ export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, e
|
|||
<td className="permissions">
|
||||
<input type="checkbox"
|
||||
onChange={setPermissions.bind(null, OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE, groupId)}
|
||||
checked={hasPermissions(group.permissions, (OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE))}/>
|
||||
checked={hasPermissions(permissions, (OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE))}/>
|
||||
</td>
|
||||
<td className="permissions">
|
||||
<input type="checkbox"
|
||||
onChange={setPermissions.bind(null, OC.PERMISSION_SHARE, groupId)}
|
||||
checked={hasPermissions(group.permissions, OC.PERMISSION_SHARE)}/>
|
||||
checked={hasPermissions(permissions, OC.PERMISSION_SHARE)}/>
|
||||
</td>
|
||||
<td className="permissions">
|
||||
<input type="checkbox"
|
||||
onChange={setPermissions.bind(null, OC.PERMISSION_DELETE, groupId)}
|
||||
checked={hasPermissions(group.permissions, (OC.PERMISSION_DELETE))}/>
|
||||
</td>
|
||||
<td className="permissions">
|
||||
<input type="checkbox"
|
||||
onChange={setManageACL.bind(null, !group.manage_acl, groupId)}
|
||||
checked={group.manage_acl}/>
|
||||
checked={hasPermissions(permissions, (OC.PERMISSION_DELETE))}/>
|
||||
</td>
|
||||
<td>
|
||||
<a onClick={removeGroup.bind(this, groupId)}>
|
||||
|
@ -79,7 +69,6 @@ export function FolderGroups({groups, allGroups = [], onAddGroup, removeGroup, e
|
|||
<th>Write</th>
|
||||
<th>Share</th>
|
||||
<th>Delete</th>
|
||||
<th>Manage ACL</th>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -77,6 +77,8 @@ class ACL extends Base {
|
|||
->addArgument('folder_id', InputArgument::REQUIRED, 'Id of the folder to configure')
|
||||
->addOption('enable', 'e', InputOption::VALUE_NONE, 'Enable advanced permissions for the folder')
|
||||
->addOption('disable', 'd', InputOption::VALUE_NONE, 'Disable advanced permissions for the folder')
|
||||
->addOption('manage-add', 'm', InputOption::VALUE_NONE, 'Add manage permission for user or group')
|
||||
->addOption('manage-remove', 'r', InputOption::VALUE_NONE, 'Remove manage permission for user or group')
|
||||
->addArgument('path', InputArgument::OPTIONAL, 'The path within the folder to set permissions for')
|
||||
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'The user to configure the permissions for')
|
||||
->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'The group to configure the permissions for')
|
||||
|
@ -122,6 +124,14 @@ class ACL extends Base {
|
|||
!$input->getOption('group')
|
||||
) {
|
||||
$this->printPermissions($input, $output, $folder);
|
||||
} else if ($input->getOption('manage-add') && ($input->getOption('user') || $input->getOption('group'))) {
|
||||
$mappingType = $input->getOption('user') ? 'user' : 'group';
|
||||
$mappingId = $input->getOption('user') ? $input->getOption('user') : $input->getOption('group');
|
||||
$this->folderManager->setManageACL($folderId, $mappingType, $mappingId, true);
|
||||
} else if ($input->getOption('manage-remove') && ($input->getOption('user') || $input->getOption('group'))) {
|
||||
$mappingType = $input->getOption('user') ? 'user' : 'group';
|
||||
$mappingId = $input->getOption('user') ? $input->getOption('user') : $input->getOption('group');
|
||||
$this->folderManager->setManageACL($folderId, $mappingType, $mappingId, false);
|
||||
} else if (!$input->getArgument('path')) {
|
||||
$output->writeln('<error><path> argument has to be set when not using --enable or --disable</error>');
|
||||
return -3;
|
||||
|
|
|
@ -59,7 +59,6 @@ class Group extends Base {
|
|||
->addArgument('folder_id', InputArgument::REQUIRED, 'Id of the folder to configure')
|
||||
->addArgument('group', InputArgument::REQUIRED, 'The group to configure')
|
||||
->addArgument('permissions', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The permissions to set for the group, leave empty for read only')
|
||||
->addOption('manage-acl', 'a', InputOption::VALUE_REQUIRED, 'The group has permission to manage advanced permissions')
|
||||
->addOption('delete', 'd', InputOption::VALUE_NONE, 'Remove access for the group');
|
||||
|
||||
parent::configure();
|
||||
|
@ -82,9 +81,6 @@ class Group extends Base {
|
|||
$this->folderManager->addApplicableGroup($folderId, $groupString);
|
||||
}
|
||||
$this->folderManager->setGroupPermissions($folderId, $groupString, $permissions);
|
||||
if ($input->hasOption('manage-acl')) {
|
||||
$this->folderManager->setManageACL($folderId, $groupString, (bool)$input->getOption('manage-acl'));
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
$output->writeln('<error>Unable to parse permissions input: ' . implode(' ', $permissionsString) . '</error>');
|
||||
|
|
|
@ -74,16 +74,19 @@ class ListCommand extends Base {
|
|||
$this->writeArrayInOutputFormat($input, $output, $folders);
|
||||
} else {
|
||||
$table = new Table($output);
|
||||
$table->setHeaders(['Folder Id', 'Name', 'Groups', 'Quota', 'Size', 'Advanced Permissions']);
|
||||
$table->setHeaders(['Folder Id', 'Name', 'Groups', 'Quota', 'Size', 'Advanced Permissions', 'Manage advanced permissions']);
|
||||
$table->setRows(array_map(function ($folder) {
|
||||
$folder['size'] = \OCP\Util::humanFileSize($folder['size']);
|
||||
$folder['quota'] = ($folder['quota'] > 0) ? \OCP\Util::humanFileSize($folder['quota']) : 'Unlimited';
|
||||
$groupStrings = array_map(function (string $groupId, array $applicable) {
|
||||
$manageAclString = $applicable['manage_acl'] ? ', acl': '';
|
||||
return $groupId . ': ' . $this->permissionsToString((int)$applicable['permissions']) . $manageAclString;
|
||||
$groupStrings = array_map(function (string $groupId, int $permissions) {
|
||||
return $groupId . ': ' . $this->permissionsToString($permissions);
|
||||
}, array_keys($folder['groups']), array_values($folder['groups']));
|
||||
$folder['groups'] = implode("\n", $groupStrings);
|
||||
$folder['acl'] = $folder['acl'] ? 'Enabled' : 'Disabled';
|
||||
$manageStrings = array_map(function ($manage) {
|
||||
return $manage['id'] . ' (' . $manage['type'] . ')';
|
||||
}, $folder['manage']);
|
||||
$folder['manage'] = implode("\n", $manageStrings);
|
||||
return $folder;
|
||||
}, $folders));
|
||||
$table->render();
|
||||
|
|
|
@ -143,8 +143,8 @@ class FolderController extends OCSController {
|
|||
* @param bool $manageAcl
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function setManageACL($id, $group, $manageAcl) {
|
||||
$this->manager->setManageACL($id, $group, $manageAcl);
|
||||
public function setManageACL($id, $mappingType, $mappingId, $manageAcl) {
|
||||
$this->manager->setManageACL($id, $mappingType, $mappingId, $manageAcl);
|
||||
return new DataResponse(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class FolderManager {
|
|||
}
|
||||
|
||||
public function getAllFoldersWithSize($rootStorageId) {
|
||||
$applicableMap = $this->getAllApplicable(false);
|
||||
$applicableMap = $this->getAllApplicable();
|
||||
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -120,15 +120,46 @@ class FolderManager {
|
|||
'groups' => isset($applicableMap[$id]) ? $applicableMap[$id] : [],
|
||||
'quota' => $row['quota'],
|
||||
'size' => $row['size'] ? $row['size'] : 0,
|
||||
'acl' => (bool)$row['acl']
|
||||
'acl' => (bool)$row['acl'],
|
||||
'manage' => $this->getManageAcl($id)
|
||||
];
|
||||
}
|
||||
|
||||
return $folderMap;
|
||||
}
|
||||
|
||||
public function getFolder($id, $rootStorageId, $onlyPermissions = true) {
|
||||
$applicableMap = $this->getAllApplicable($onlyPermissions);
|
||||
private function getManageAcl($folderId) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from('group_folders_manage')
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
|
||||
$result = $query->execute()->fetchAll();
|
||||
return array_filter(array_map(function ($entry) {
|
||||
if ($entry['mapping_type'] === 'user') {
|
||||
$user = \OC::$server->getUserManager()->get($entry['mapping_id']);
|
||||
if ($user === null) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'type' => 'user',
|
||||
'id' => $user->getUID(),
|
||||
'displayname' => $user->getDisplayName()
|
||||
];
|
||||
}
|
||||
$group = \OC::$server->getGroupManager()->get($entry['mapping_id']);
|
||||
if ($group === null) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
'type' => 'group',
|
||||
'id' => $group->getGID(),
|
||||
'displayname' => $group->getDisplayName()
|
||||
];
|
||||
}, $result), function($element) { return $element !== null; });
|
||||
}
|
||||
|
||||
public function getFolder($id, $rootStorageId) {
|
||||
$applicableMap = $this->getAllApplicable();
|
||||
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -159,7 +190,7 @@ class FolderManager {
|
|||
private function getAllApplicable(bool $permissionOnly = true) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
$query->select('folder_id', 'group_id', 'permissions', 'manage_acl')
|
||||
$query->select('folder_id', 'group_id', 'permissions')
|
||||
->from('group_folders_groups');
|
||||
|
||||
$rows = $query->execute()->fetchAll();
|
||||
|
@ -170,12 +201,7 @@ class FolderManager {
|
|||
if (!isset($applicableMap[$id])) {
|
||||
$applicableMap[$id] = [];
|
||||
}
|
||||
if ($permissionOnly) {
|
||||
$applicableMap[$id][$row['group_id']] = (int)$row['permissions'];
|
||||
} else {
|
||||
$applicableMap[$id][$row['group_id']]['permissions'] = (int)$row['permissions'];
|
||||
$applicableMap[$id][$row['group_id']]['manage_acl'] = (bool)$row['manage_acl'];
|
||||
}
|
||||
$applicableMap[$id][$row['group_id']] = (int)$row['permissions'];
|
||||
}
|
||||
|
||||
return $applicableMap;
|
||||
|
@ -193,11 +219,28 @@ class FolderManager {
|
|||
}
|
||||
|
||||
public function canManageACL($folderId, $userId): bool {
|
||||
$folder = $this->getFolder($folderId, -1, false);
|
||||
$groups = $folder['groups'];
|
||||
foreach ($groups as $group => $groupDetails) {
|
||||
$canManageACL = $groupDetails['manage_acl'] ?? false;
|
||||
if ((bool)$canManageACL === true && $this->groupManager->isInGroup($userId, $group)) {
|
||||
if ($this->groupManager->isAdmin($userId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from('group_folders_manage')
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)))
|
||||
->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('user')))
|
||||
->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($userId)));
|
||||
if ($query->execute()->rowCount() === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from('group_folders_manage')
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)))
|
||||
->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('group')));
|
||||
$groups = $query->execute()->fetchAll();
|
||||
foreach ($groups as $manageRule) {
|
||||
if ($this->groupManager->isInGroup($userId, $manageRule['mapping_id'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -321,14 +364,21 @@ class FolderManager {
|
|||
$query->execute();
|
||||
}
|
||||
|
||||
public function setManageACL($folderId, $groupId, $manageAcl) {
|
||||
public function setManageACL($folderId, $type, $id, $manageAcl) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
$query->update('group_folders_groups')
|
||||
->set('manage_acl', $query->createNamedParameter($manageAcl, IQueryBuilder::PARAM_BOOL))
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('group_id', $query->createNamedParameter($groupId)));
|
||||
|
||||
if ($manageAcl === true) {
|
||||
$query->insert('group_folders_manage')
|
||||
->values([
|
||||
'folder_id' => $query->createNamedParameter($folderId),
|
||||
'mapping_type' => $query->createNamedParameter($type),
|
||||
'mapping_id' => $query->createNamedParameter($id)
|
||||
]);
|
||||
} else {
|
||||
$query->delete('group_folders_manage')
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)))
|
||||
->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter($type)))
|
||||
->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($id)));
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
|
@ -373,6 +423,13 @@ class FolderManager {
|
|||
->set('acl', $query->createNamedParameter((int)$acl, IQueryBuilder::PARAM_INT))
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
|
||||
$query->execute();
|
||||
|
||||
if ($acl === false) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->delete('group_folders_manage')
|
||||
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\GroupFolders\Migration;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use OCP\Migration\IOutput;
|
||||
|
||||
/**
|
||||
* Auto-generated migration step: Please modify to your needs!
|
||||
*/
|
||||
class Version401000Date20190715092137 extends SimpleMigrationStep {
|
||||
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$table = $schema->getTable('group_folders_groups');
|
||||
if (!$table->hasColumn('manage_acl')) {
|
||||
$table->addColumn('manage_acl', Type::BOOLEAN, [
|
||||
'notnull' => true,
|
||||
'default' => false
|
||||
]);
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\GroupFolders\Migration;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use OCP\Migration\IOutput;
|
||||
|
||||
/**
|
||||
* Auto-generated migration step: Please modify to your needs!
|
||||
*/
|
||||
class Version401001Date20190715092137 extends SimpleMigrationStep {
|
||||
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if (!$schema->hasTable('group_folders_manage')) {
|
||||
$table = $schema->createTable('group_folders_manage');
|
||||
$table->addColumn('folder_id', 'bigint', [
|
||||
'notnull' => true,
|
||||
'length' => 6,
|
||||
]);
|
||||
$table->addColumn('mapping_type', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 16,
|
||||
]);
|
||||
$table->addColumn('mapping_id', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 64,
|
||||
]);
|
||||
$table->setPrimaryKey(['folder_id', 'mapping_type', 'mapping_id']);
|
||||
$table->addUniqueIndex(['folder_id', 'mapping_type', 'mapping_id'], 'groups_folder_manage_unique');
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue