mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
More updates to HDFS Manage Access dialog (#7611)
* Add display property to ModelView components * Update DisplayType property in sqlops as well * More updates to HDFS Manage Access dialog * More updates to HDFS Manage Access dialog
This commit is contained in:
@@ -3,38 +3,44 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IconPathHelper, IconPath } from '../iconHelper';
|
||||
|
||||
/**
|
||||
* The parsed result from calling getAclStatus on the controller
|
||||
* The permission status of an HDFS path - this consists of :
|
||||
* - The sticky bit for that path
|
||||
* - The permission bits for the owner, group and other
|
||||
* - (Optional) Set of additional ACL entries on this path
|
||||
*/
|
||||
export interface IAclStatus {
|
||||
export class PermissionStatus {
|
||||
/**
|
||||
* The ACL entries defined for the object
|
||||
*/
|
||||
entries: AclEntry[];
|
||||
/**
|
||||
* The ACL entry object for the owner permissions
|
||||
*/
|
||||
owner: AclEntry;
|
||||
/**
|
||||
* The ACL entry object for the group permissions
|
||||
*/
|
||||
group: AclEntry;
|
||||
/**
|
||||
* The ACL entry object for the other permissions
|
||||
*/
|
||||
other: AclEntry;
|
||||
/**
|
||||
* The sticky bit status for the object. If true the owner/root are
|
||||
*
|
||||
* @param owner The ACL entry object for the owner permissions
|
||||
* @param group The ACL entry object for the group permissions
|
||||
* @param other The ACL entry object for the other permissions
|
||||
* @param stickyBit The sticky bit status for the object. If true the owner/root are
|
||||
* the only ones who can delete the resource or its contents (if a folder)
|
||||
* @param aclEntries The ACL entries defined for the object
|
||||
*/
|
||||
stickyBit: boolean;
|
||||
constructor(public owner: AclEntry, public group: AclEntry, public other: AclEntry, public stickyBit: boolean, public aclEntries: AclEntry[]) { }
|
||||
|
||||
/**
|
||||
* The permission octal for the path in the form [#]### with each # mapping to :
|
||||
* 0 (optional) - The sticky bit (1 or 0)
|
||||
* 1 - The owner permission digit
|
||||
* 2 - The group permission digit
|
||||
* 3 - The other permission digit
|
||||
* @see AclEntryPermission for more information on the permission digits
|
||||
*/
|
||||
public get permissionOctal(): string {
|
||||
return `${this.stickyBit ? '1' : ''}${this.owner.permissionDigit}${this.group.permissionDigit}${this.other.permissionDigit}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of an ACL entry. Corresponds to the first (or second if a scope is present) field of
|
||||
* an ACL entry - e.g. user:bob:rwx (user) or default:group::r-- (group)
|
||||
*/
|
||||
export enum AclEntryType {
|
||||
export enum AclType {
|
||||
/**
|
||||
* An ACL entry applied to a specific user.
|
||||
*/
|
||||
@@ -58,7 +64,7 @@ export enum AclEntryType {
|
||||
* Typically this value is represented as a 3 digit octal - e.g. 740 - where the first digit is the owner, the second
|
||||
* the group and the third other. @see parseAclPermissionFromOctal
|
||||
*/
|
||||
export enum AclPermissionType {
|
||||
export enum PermissionType {
|
||||
owner = 'owner',
|
||||
group = 'group',
|
||||
other = 'other'
|
||||
@@ -92,6 +98,14 @@ export class AclEntryPermission {
|
||||
public toString() {
|
||||
return `${this.read ? 'r' : '-'}${this.write ? 'w' : '-'}${this.execute ? 'x' : '-'}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the digit for a permission octal for this permission. This digit is a value
|
||||
* between 0 and 7 inclusive, which is a bitwise OR the permission flags (r/w/x).
|
||||
*/
|
||||
public get permissionDigit(): number {
|
||||
return (this.read ? 4 : 0) + (this.write ? 2 : 0) + (this.execute ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,12 +137,19 @@ function parseAclPermission(permissionString: string): AclEntryPermission {
|
||||
export class AclEntry {
|
||||
constructor(
|
||||
public readonly scope: AclEntryScope,
|
||||
public readonly type: AclEntryType | AclPermissionType,
|
||||
public readonly type: AclType | PermissionType,
|
||||
public readonly name: string,
|
||||
public readonly displayName: string,
|
||||
public readonly permission: AclEntryPermission,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Gets the octal number representing the permission for this entry. This digit is a value
|
||||
* between 0 and 7 inclusive, which is a bitwise OR the permission flags (r/w/x).
|
||||
*/
|
||||
public get permissionDigit(): number {
|
||||
return this.permission.permissionDigit;
|
||||
}
|
||||
/**
|
||||
* Returns the string representation of the ACL Entry in the form [SCOPE:]TYPE:NAME:PERMISSION.
|
||||
* Note that SCOPE is only displayed if it's default - access is implied if there is no scope
|
||||
@@ -163,22 +184,22 @@ export class AclEntry {
|
||||
* Maps the possible entry types into their corresponding values for using in an ACL string
|
||||
* @param type The type to convert
|
||||
*/
|
||||
function getAclEntryType(type: AclEntryType | AclPermissionType): AclEntryType {
|
||||
function getAclEntryType(type: AclType | PermissionType): AclType {
|
||||
// We only need to map AclPermissionType - AclEntryType is already the
|
||||
// correct values we're mapping to.
|
||||
if (type in AclPermissionType) {
|
||||
if (type in PermissionType) {
|
||||
switch (type) {
|
||||
case AclPermissionType.owner:
|
||||
return AclEntryType.user;
|
||||
case AclPermissionType.group:
|
||||
return AclEntryType.group;
|
||||
case AclPermissionType.other:
|
||||
return AclEntryType.other;
|
||||
case PermissionType.owner:
|
||||
return AclType.user;
|
||||
case PermissionType.group:
|
||||
return AclType.group;
|
||||
case PermissionType.other:
|
||||
return AclType.other;
|
||||
default:
|
||||
throw new Error(`Unknown AclPermissionType : ${type}`);
|
||||
}
|
||||
}
|
||||
return <AclEntryType>type;
|
||||
return <AclType>type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,19 +234,19 @@ export function parseAclEntry(aclString: string): AclEntry {
|
||||
const parts: string[] = aclString.split(':');
|
||||
let i = 0;
|
||||
const scope: AclEntryScope = parts.length === 4 && parts[i++] === 'default' ? AclEntryScope.default : AclEntryScope.access;
|
||||
let type: AclEntryType;
|
||||
let type: AclType;
|
||||
switch (parts[i++]) {
|
||||
case 'user':
|
||||
type = AclEntryType.user;
|
||||
type = AclType.user;
|
||||
break;
|
||||
case 'group':
|
||||
type = AclEntryType.group;
|
||||
type = AclType.group;
|
||||
break;
|
||||
case 'mask':
|
||||
type = AclEntryType.mask;
|
||||
type = AclType.mask;
|
||||
break;
|
||||
case 'other':
|
||||
type = AclEntryType.other;
|
||||
type = AclType.other;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown ACL Entry type ${parts[i - 1]}`);
|
||||
@@ -269,3 +290,16 @@ export function parseAclPermissionFromOctal(octal: string): { sticky: boolean, o
|
||||
other: new AclEntryPermission((otherPermissionDigit & 4) === 4, (otherPermissionDigit & 2) === 2, (otherPermissionDigit & 1) === 1)
|
||||
};
|
||||
}
|
||||
|
||||
export function getImageForType(type: AclType | PermissionType): IconPath {
|
||||
switch (type) {
|
||||
case AclType.user:
|
||||
case PermissionType.owner:
|
||||
return IconPathHelper.user;
|
||||
case AclType.group:
|
||||
case PermissionType.group:
|
||||
case PermissionType.other:
|
||||
return IconPathHelper.group;
|
||||
}
|
||||
return { dark: '', light: '' };
|
||||
}
|
||||
|
||||
85
extensions/mssql/src/hdfs/fileStatus.ts
Normal file
85
extensions/mssql/src/hdfs/fileStatus.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export enum HdfsFileType {
|
||||
File = 'File',
|
||||
Directory = 'Directory',
|
||||
Symlink = 'Symlink'
|
||||
}
|
||||
|
||||
export class FileStatus {
|
||||
/**
|
||||
*
|
||||
* @param owner The ACL entry object for the owner permissions
|
||||
* @param group The ACL entry object for the group permissions
|
||||
* @param other The ACL entry object for the other permissions
|
||||
* @param stickyBit The sticky bit status for the object. If true the owner/root are
|
||||
* the only ones who can delete the resource or its contents (if a folder)
|
||||
* @param aclEntries The ACL entries defined for the object
|
||||
*/
|
||||
constructor(
|
||||
/**
|
||||
* Access time for the file
|
||||
*/
|
||||
public readonly accessTime: string,
|
||||
/**
|
||||
* The block size of a file.
|
||||
*/
|
||||
public readonly blockSize: string,
|
||||
/**
|
||||
* The group owner.
|
||||
*/
|
||||
public readonly group: string,
|
||||
/**
|
||||
* The number of bytes in a file. (0 for directories)
|
||||
*/
|
||||
public readonly length: string,
|
||||
/**
|
||||
* The modification time.
|
||||
*/
|
||||
public readonly modificationTime: string,
|
||||
/**
|
||||
* The user who is the owner.
|
||||
*/
|
||||
public readonly owner: string,
|
||||
/**
|
||||
* The path suffix.
|
||||
*/
|
||||
public readonly pathSuffix: string,
|
||||
/**
|
||||
* The permission represented as a octal string.
|
||||
*/
|
||||
public readonly permission: string,
|
||||
/**
|
||||
* The number of replication of a file.
|
||||
*/
|
||||
public readonly replication: string,
|
||||
/**
|
||||
* Whether a directory is snapshot enabled or not
|
||||
*/
|
||||
public readonly snapshotEnabled: string,
|
||||
/**
|
||||
* The type of the path object.
|
||||
*/
|
||||
public readonly type: HdfsFileType
|
||||
) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a fileType string into the corresponding @see HdfsFileType
|
||||
* @param fileType The fileType string to parse
|
||||
*/
|
||||
export function parseHdfsFileType(fileType: string): HdfsFileType {
|
||||
switch (fileType.toLowerCase()) {
|
||||
case 'file':
|
||||
return HdfsFileType.File;
|
||||
case 'directory':
|
||||
return HdfsFileType.Directory;
|
||||
case 'symlink':
|
||||
return HdfsFileType.Symlink;
|
||||
default:
|
||||
throw new Error(`Unknown HdfsFileType '${fileType}'`);
|
||||
}
|
||||
}
|
||||
@@ -5,23 +5,29 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { IFileSource } from '../objectExplorerNodeProvider/fileSources';
|
||||
import { IAclStatus, AclEntry, AclEntryScope, AclEntryType, AclEntryPermission } from './aclEntry';
|
||||
import { PermissionStatus, AclEntry, AclEntryScope, AclType, AclEntryPermission } from './aclEntry';
|
||||
import { FileStatus } from './fileStatus';
|
||||
|
||||
/**
|
||||
* Model for storing the state of a specified file/folder in HDFS
|
||||
*/
|
||||
export class HdfsModel {
|
||||
|
||||
private readonly _onAclStatusUpdated = new vscode.EventEmitter<IAclStatus>();
|
||||
private readonly _onPermissionStatusUpdated = new vscode.EventEmitter<PermissionStatus>();
|
||||
/**
|
||||
* Event that's fired anytime changes are made by the model to the ACLStatus
|
||||
* Event that's fired anytime changes are made by the model to the @see PermissionStatus
|
||||
*/
|
||||
public onAclStatusUpdated = this._onAclStatusUpdated.event;
|
||||
public onPermissionStatusUpdated = this._onPermissionStatusUpdated.event;
|
||||
|
||||
/**
|
||||
* The ACL status of the file/folder
|
||||
* The @see PermissionStatus of the file/folder
|
||||
*/
|
||||
public aclStatus: IAclStatus;
|
||||
public permissionStatus: PermissionStatus;
|
||||
|
||||
/**
|
||||
* The @see FileStatus of the file/folder
|
||||
*/
|
||||
public fileStatus: FileStatus;
|
||||
|
||||
constructor(private fileSource: IFileSource, private path: string) {
|
||||
this.refresh();
|
||||
@@ -31,8 +37,10 @@ export class HdfsModel {
|
||||
* Refresh the ACL status with the current values on HDFS
|
||||
*/
|
||||
public async refresh(): Promise<void> {
|
||||
this.aclStatus = await this.fileSource.getAclStatus(this.path);
|
||||
this._onAclStatusUpdated.fire(this.aclStatus);
|
||||
[this.permissionStatus, this.fileStatus] = await Promise.all([
|
||||
this.fileSource.getAclStatus(this.path),
|
||||
this.fileSource.getFileStatus(this.path)]);
|
||||
this._onPermissionStatusUpdated.fire(this.permissionStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,18 +49,18 @@ export class HdfsModel {
|
||||
* @param name The name of the ACL Entry
|
||||
* @param type The type of ACL to create
|
||||
*/
|
||||
public createAndAddAclEntry(name: string, type: AclEntryType): void {
|
||||
if (!this.aclStatus) {
|
||||
public createAndAddAclEntry(name: string, type: AclType): void {
|
||||
if (!this.permissionStatus) {
|
||||
return;
|
||||
}
|
||||
const newEntry = new AclEntry(AclEntryScope.access, type, name, name, new AclEntryPermission(true, true, true));
|
||||
// Don't add duplicates. This also checks the owner, group and other items
|
||||
if ([this.aclStatus.owner, this.aclStatus.group, this.aclStatus.other].concat(this.aclStatus.entries).find(entry => entry.isEqual(newEntry))) {
|
||||
if ([this.permissionStatus.owner, this.permissionStatus.group, this.permissionStatus.other].concat(this.permissionStatus.aclEntries).find(entry => entry.isEqual(newEntry))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.aclStatus.entries.push(newEntry);
|
||||
this._onAclStatusUpdated.fire(this.aclStatus);
|
||||
this.permissionStatus.aclEntries.push(newEntry);
|
||||
this._onPermissionStatusUpdated.fire(this.permissionStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +68,8 @@ export class HdfsModel {
|
||||
* @param entryToDelete The entry to delete
|
||||
*/
|
||||
public deleteAclEntry(entryToDelete: AclEntry): void {
|
||||
this.aclStatus.entries = this.aclStatus.entries.filter(entry => !entry.isEqual(entryToDelete));
|
||||
this._onAclStatusUpdated.fire(this.aclStatus);
|
||||
this.permissionStatus.aclEntries = this.permissionStatus.aclEntries.filter(entry => !entry.isEqual(entryToDelete));
|
||||
this._onPermissionStatusUpdated.fire(this.permissionStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +78,10 @@ export class HdfsModel {
|
||||
* permissions that shouldn't change need to still exist and have the same values.
|
||||
* @param recursive Whether to apply the changes recursively (to all sub-folders and files)
|
||||
*/
|
||||
public apply(recursive: boolean = false): Promise<void> {
|
||||
public apply(recursive: boolean = false): Promise<any> {
|
||||
// TODO Apply recursive
|
||||
return this.fileSource.setAcl(this.path, this.aclStatus.owner, this.aclStatus.group, this.aclStatus.other, this.aclStatus.entries);
|
||||
return Promise.all([
|
||||
this.fileSource.setAcl(this.path, this.permissionStatus.owner, this.permissionStatus.group, this.permissionStatus.other, this.permissionStatus.aclEntries),
|
||||
this.fileSource.setPermission(this.path, this.permissionStatus)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
import * as azdata from 'azdata';
|
||||
import { HdfsModel } from '../hdfsModel';
|
||||
import { IFileSource } from '../../objectExplorerNodeProvider/fileSources';
|
||||
import { IAclStatus, AclEntry, AclEntryType } from '../../hdfs/aclEntry';
|
||||
import { PermissionStatus, AclEntry, AclType, getImageForType } from '../../hdfs/aclEntry';
|
||||
import { cssStyles } from './uiConstants';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { HdfsError } from '../webhdfs';
|
||||
import { ApiWrapper } from '../../apiWrapper';
|
||||
import { IconPathHelper } from '../../iconHelper';
|
||||
import { HdfsFileType } from '../fileStatus';
|
||||
|
||||
const permissionsTypeIconColumnWidth = 35;
|
||||
const permissionsNameColumnWidth = 250;
|
||||
const permissionsStickyColumnWidth = 50;
|
||||
const permissionsReadColumnWidth = 50;
|
||||
@@ -36,11 +38,13 @@ export class ManageAccessDialog {
|
||||
private addUserOrGroupInput: azdata.InputBoxComponent;
|
||||
private dialog: azdata.window.Dialog;
|
||||
|
||||
private addUserOrGroupSelectedType: AclEntryType;
|
||||
private defaultSectionComponents: azdata.Component[] = [];
|
||||
|
||||
private addUserOrGroupSelectedType: AclType;
|
||||
|
||||
constructor(private hdfsPath: string, private fileSource: IFileSource, private readonly apiWrapper: ApiWrapper) {
|
||||
this.hdfsModel = new HdfsModel(this.fileSource, this.hdfsPath);
|
||||
this.hdfsModel.onAclStatusUpdated(aclStatus => this.handleAclStatusUpdated(aclStatus));
|
||||
this.hdfsModel.onPermissionStatusUpdated(permissionStatus => this.handlePermissionStatusUpdated(permissionStatus));
|
||||
}
|
||||
|
||||
public openDialog(): void {
|
||||
@@ -123,7 +127,7 @@ export class ManageAccessDialog {
|
||||
// = Owners permissions section =
|
||||
// ==============================
|
||||
|
||||
const ownersPermissionsHeaderRow = createPermissionsHeaderRow(modelView.modelBuilder, loc.ownersHeader, true);
|
||||
const ownersPermissionsHeaderRow = this.createPermissionsHeaderRow(modelView.modelBuilder, loc.ownersHeader, true);
|
||||
contentContainer.addItem(ownersPermissionsHeaderRow, { CSSStyles: { ...cssStyles.tableHeaderLayoutCss } });
|
||||
|
||||
// Empty initially - this is going to eventually be populated with the owner/owning group permissions
|
||||
@@ -155,10 +159,10 @@ export class ManageAccessDialog {
|
||||
|
||||
const typeContainer = modelView.modelBuilder.flexContainer().withProperties({ flexFlow: 'row' }).component();
|
||||
const aclEntryTypeGroup = 'aclEntryType';
|
||||
const userTypeButton = this.createRadioButton(modelView.modelBuilder, loc.userLabel, aclEntryTypeGroup, AclEntryType.user);
|
||||
const groupTypeButton = this.createRadioButton(modelView.modelBuilder, loc.groupLabel, aclEntryTypeGroup, AclEntryType.group);
|
||||
const userTypeButton = this.createRadioButton(modelView.modelBuilder, loc.userLabel, aclEntryTypeGroup, AclType.user);
|
||||
const groupTypeButton = this.createRadioButton(modelView.modelBuilder, loc.groupLabel, aclEntryTypeGroup, AclType.group);
|
||||
userTypeButton.checked = true;
|
||||
this.addUserOrGroupSelectedType = AclEntryType.user;
|
||||
this.addUserOrGroupSelectedType = AclType.user;
|
||||
|
||||
typeContainer.addItems([userTypeButton, groupTypeButton], { flex: '0 0 auto' });
|
||||
contentContainer.addItem(typeContainer, { flex: '0 0 auto' });
|
||||
@@ -199,7 +203,7 @@ export class ManageAccessDialog {
|
||||
// = Named Users and Groups permissions header row =
|
||||
// =================================================
|
||||
|
||||
const namedUsersAndGroupsPermissionsHeaderRow = createPermissionsHeaderRow(modelView.modelBuilder, loc.namedUsersAndGroupsHeader, false);
|
||||
const namedUsersAndGroupsPermissionsHeaderRow = this.createPermissionsHeaderRow(modelView.modelBuilder, loc.namedUsersAndGroupsHeader, false);
|
||||
contentContainer.addItem(namedUsersAndGroupsPermissionsHeaderRow, { CSSStyles: { ...cssStyles.tableHeaderLayoutCss } });
|
||||
|
||||
// Empty initially - this is eventually going to be populated with the ACL entries set for this path
|
||||
@@ -209,7 +213,7 @@ export class ManageAccessDialog {
|
||||
contentContainer.addItem(this.namedUsersAndGroupsPermissionsContainer, { flex: '1', CSSStyles: { 'overflow': 'scroll', 'min-height': '200px' } });
|
||||
|
||||
this.viewInitialized = true;
|
||||
this.handleAclStatusUpdated(this.hdfsModel.aclStatus);
|
||||
this.handlePermissionStatusUpdated(this.hdfsModel.permissionStatus);
|
||||
await modelView.initializeModel(this.rootLoadingComponent);
|
||||
});
|
||||
this.dialog.content = [tab];
|
||||
@@ -218,25 +222,28 @@ export class ManageAccessDialog {
|
||||
azdata.window.openDialog(this.dialog);
|
||||
}
|
||||
|
||||
private handleAclStatusUpdated(aclStatus: IAclStatus): void {
|
||||
if (!aclStatus || !this.viewInitialized) {
|
||||
private handlePermissionStatusUpdated(permissionStatus: PermissionStatus): void {
|
||||
if (!permissionStatus || !this.viewInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update display status for headers for the Default section - you can't set Default ACLs for non-directories so we just hide that column
|
||||
this.defaultSectionComponents.forEach(component => component.display = this.hdfsModel.fileStatus.type === HdfsFileType.Directory ? '' : 'none');
|
||||
|
||||
// Owners
|
||||
const ownerPermissionsRow = this.createOwnerPermissionsRow(this.modelBuilder, aclStatus.stickyBit, aclStatus.owner);
|
||||
const owningGroupPermissionsRow = this.createPermissionsRow(this.modelBuilder, aclStatus.group, false);
|
||||
const ownerPermissionsRow = this.createOwnerPermissionsRow(this.modelBuilder, permissionStatus);
|
||||
const owningGroupPermissionsRow = this.createPermissionsRow(this.modelBuilder, permissionStatus.group, /*includeDelete*/false);
|
||||
this.ownersPermissionsContainer.clearItems();
|
||||
this.ownersPermissionsContainer.addItems([ownerPermissionsRow, owningGroupPermissionsRow], { CSSStyles: { 'border-bottom': cssStyles.tableBorderCss, 'border-top': cssStyles.tableBorderCss, 'margin-right': '14px' } });
|
||||
|
||||
// Others
|
||||
const otherPermissionsRow = this.createPermissionsRow(this.modelBuilder, aclStatus.other, false);
|
||||
const otherPermissionsRow = this.createPermissionsRow(this.modelBuilder, permissionStatus.other, false);
|
||||
this.othersPermissionsContainer.clearItems();
|
||||
this.othersPermissionsContainer.addItem(otherPermissionsRow, { CSSStyles: { 'border-bottom': cssStyles.tableBorderCss, 'border-top': cssStyles.tableBorderCss, 'margin-right': '14px' } });
|
||||
|
||||
this.namedUsersAndGroupsPermissionsContainer.clearItems();
|
||||
// Named users and groups
|
||||
aclStatus.entries.forEach(entry => {
|
||||
permissionStatus.aclEntries.forEach(entry => {
|
||||
const namedEntryRow = this.createPermissionsRow(this.modelBuilder, entry, true);
|
||||
this.namedUsersAndGroupsPermissionsContainer.addItem(namedEntryRow, { CSSStyles: { 'border-bottom': cssStyles.tableBorderCss, 'border-top': cssStyles.tableBorderCss } });
|
||||
});
|
||||
@@ -244,7 +251,7 @@ export class ManageAccessDialog {
|
||||
this.rootLoadingComponent.loading = false;
|
||||
}
|
||||
|
||||
private createRadioButton(modelBuilder: azdata.ModelBuilder, label: string, name: string, aclEntryType: AclEntryType): azdata.RadioButtonComponent {
|
||||
private createRadioButton(modelBuilder: azdata.ModelBuilder, label: string, name: string, aclEntryType: AclType): azdata.RadioButtonComponent {
|
||||
const button = modelBuilder.radioButton().withProperties<azdata.RadioButtonProperties>({ label: label, name: name }).component();
|
||||
button.onDidClick(() => {
|
||||
this.addUserOrGroupSelectedType = aclEntryType;
|
||||
@@ -252,16 +259,33 @@ export class ManageAccessDialog {
|
||||
return button;
|
||||
}
|
||||
|
||||
private createOwnerPermissionsRow(builder: azdata.ModelBuilder, sticky: boolean, entry: AclEntry): azdata.FlexContainer {
|
||||
const row = this.createPermissionsRow(builder, entry, false);
|
||||
const stickyComponents = createCheckbox(builder, sticky, permissionsReadColumnWidth, permissionsRowHeight);
|
||||
private createOwnerPermissionsRow(builder: azdata.ModelBuilder, permissionStatus: PermissionStatus): azdata.FlexContainer {
|
||||
const row = this.createPermissionsRow(builder, permissionStatus.owner, false);
|
||||
const stickyComponents = createCheckbox(builder, permissionStatus.stickyBit, permissionsReadColumnWidth, permissionsRowHeight);
|
||||
stickyComponents.checkbox.onChanged(() => {
|
||||
permissionStatus.stickyBit = stickyComponents.checkbox.checked;
|
||||
});
|
||||
// Insert after name item but before other checkboxes
|
||||
row.insertItem(stickyComponents.container, 1, { flex: '0 0 auto' });
|
||||
row.insertItem(stickyComponents.container, 2, { flex: '0 0 auto' });
|
||||
return row;
|
||||
}
|
||||
|
||||
private createPermissionsRow(builder: azdata.ModelBuilder, entry: AclEntry, includeDelete: boolean): azdata.FlexContainer {
|
||||
const rowContainer = builder.flexContainer().withLayout({ flexFlow: 'row', height: permissionsRowHeight }).component();
|
||||
|
||||
// Icon
|
||||
const iconCell = builder.image()
|
||||
.withProperties<azdata.ImageComponentProperties>({
|
||||
iconPath: getImageForType(entry.type),
|
||||
width: permissionsTypeIconColumnWidth,
|
||||
height: permissionsRowHeight,
|
||||
iconWidth: 20,
|
||||
iconHeight: 20
|
||||
})
|
||||
.component();
|
||||
rowContainer.addItem(iconCell, { flex: '0 0 auto' });
|
||||
|
||||
// Name
|
||||
const nameCell = builder.text().withProperties({ value: entry.displayName }).component();
|
||||
rowContainer.addItem(nameCell);
|
||||
|
||||
@@ -281,31 +305,35 @@ export class ManageAccessDialog {
|
||||
|
||||
// Access - Execute
|
||||
const accessExecuteComponents = createCheckbox(builder, entry.permission.execute, permissionsExecuteColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(accessExecuteComponents.container, { flex: '0 0 auto', CSSStyles: { 'border-right': cssStyles.tableBorderCss } });
|
||||
rowContainer.addItem(accessExecuteComponents.container, { flex: '0 0 auto', CSSStyles: { 'border-right': this.hdfsModel.fileStatus.type === HdfsFileType.Directory ? cssStyles.tableBorderCss : '' } });
|
||||
accessExecuteComponents.checkbox.onChanged(() => {
|
||||
entry.permission.execute = accessExecuteComponents.checkbox.checked;
|
||||
});
|
||||
|
||||
// Default - Read
|
||||
const defaultReadComponents = createCheckbox(builder, false, permissionsReadColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(defaultReadComponents.container, { flex: '0 0 auto' });
|
||||
defaultReadComponents.checkbox.onChanged(() => {
|
||||
// entry.permission.read = defaultReadComponents.checkbox.checked; TODO hook up default logic
|
||||
});
|
||||
// Only directories can set ACL defaults so we hide the column for non-directories
|
||||
if (this.hdfsModel.fileStatus.type === HdfsFileType.Directory) {
|
||||
// Default - Read
|
||||
const defaultReadComponents = createCheckbox(builder, false, permissionsReadColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(defaultReadComponents.container, { flex: '0 0 auto' });
|
||||
defaultReadComponents.checkbox.onChanged(() => {
|
||||
// entry.permission.read = defaultReadComponents.checkbox.checked; TODO hook up default logic
|
||||
});
|
||||
|
||||
// Default - Write
|
||||
const defaultWriteComponents = createCheckbox(builder, false, permissionsWriteColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(defaultWriteComponents.container, { flex: '0 0 auto' });
|
||||
accessReadComponents.checkbox.onChanged(() => {
|
||||
// entry.permission.write = accessReadComponents.checkbox.checked; TODO hook up default logic
|
||||
});
|
||||
// Default - Write
|
||||
const defaultWriteComponents = createCheckbox(builder, false, permissionsWriteColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(defaultWriteComponents.container, { flex: '0 0 auto' });
|
||||
accessReadComponents.checkbox.onChanged(() => {
|
||||
// entry.permission.write = accessReadComponents.checkbox.checked; TODO hook up default logic
|
||||
});
|
||||
|
||||
// Default - Execute
|
||||
const defaultExecuteComponents = createCheckbox(builder, false, permissionsExecuteColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(defaultExecuteComponents.container, { flex: '0 0 auto' });
|
||||
accessReadComponents.checkbox.onChanged(() => {
|
||||
// entry.permission.execute = accessReadComponents.checkbox.checked; TODO hook up default logic
|
||||
});
|
||||
}
|
||||
|
||||
// Default - Execute
|
||||
const defaultExecuteComponents = createCheckbox(builder, false, permissionsExecuteColumnWidth, permissionsRowHeight);
|
||||
rowContainer.addItem(defaultExecuteComponents.container, { flex: '0 0 auto' });
|
||||
accessReadComponents.checkbox.onChanged(() => {
|
||||
// entry.permission.execute = accessReadComponents.checkbox.checked; TODO hook up default logic
|
||||
});
|
||||
|
||||
const deleteContainer = builder.flexContainer().withLayout({ width: permissionsDeleteColumnWidth, height: permissionsRowHeight }).component();
|
||||
|
||||
@@ -327,60 +355,90 @@ export class ManageAccessDialog {
|
||||
|
||||
return rowContainer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the header row for the permissions tables. This contains headers for the name, optional sticky and then read/write/execute for both
|
||||
* access and default sections.
|
||||
* @param modelBuilder The builder used to create the model components
|
||||
* @param nameColumnText The text to display for the name column
|
||||
* @param includeSticky Whether to include the sticky header
|
||||
*/
|
||||
function createPermissionsHeaderRow(modelBuilder: azdata.ModelBuilder, nameColumnText: string, includeSticky: boolean): azdata.FlexContainer {
|
||||
const rowsContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
||||
/**
|
||||
* Creates the header row for the permissions tables. This contains headers for the name, optional sticky and then read/write/execute for both
|
||||
* access and default sections.
|
||||
* @param modelBuilder The builder used to create the model components
|
||||
* @param nameColumnText The text to display for the name column
|
||||
* @param includeSticky Whether to include the sticky header
|
||||
*/
|
||||
private createPermissionsHeaderRow(modelBuilder: azdata.ModelBuilder, nameColumnText: string, includeSticky: boolean): azdata.FlexContainer {
|
||||
const rowsContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
||||
|
||||
// Section Headers
|
||||
const sectionHeaderContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'row', justifyContent: 'flex-end' }).component();
|
||||
const accessSectionHeader = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.accessHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
sectionHeaderContainer.addItem(accessSectionHeader, { CSSStyles: { 'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px` } });
|
||||
const defaultSectionHeader = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.defaultHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
sectionHeaderContainer.addItem(defaultSectionHeader, { CSSStyles: { 'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px` } });
|
||||
// Delete - just used as a spacer
|
||||
const deleteSectionHeader = modelBuilder.text().component();
|
||||
sectionHeaderContainer.addItem(deleteSectionHeader, { CSSStyles: { 'width': `${permissionsDeleteColumnWidth}px`, 'min-width': `${permissionsDeleteColumnWidth}px` } });
|
||||
// Section Headers
|
||||
const sectionHeaderContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'row', justifyContent: 'flex-end' }).component();
|
||||
const accessSectionHeader = modelBuilder.text()
|
||||
.withProperties<azdata.TextComponentProperties>({
|
||||
value: loc.accessHeader,
|
||||
CSSStyles: {
|
||||
'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`,
|
||||
'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`,
|
||||
...cssStyles.permissionsTableHeaderCss
|
||||
}
|
||||
})
|
||||
.component();
|
||||
|
||||
rowsContainer.addItem(sectionHeaderContainer);
|
||||
sectionHeaderContainer.addItem(accessSectionHeader, { flex: '0 0 auto' });
|
||||
const defaultSectionHeader = modelBuilder.text()
|
||||
.withProperties<azdata.TextComponentProperties>({
|
||||
value: loc.defaultHeader,
|
||||
CSSStyles: {
|
||||
'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`,
|
||||
'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`,
|
||||
...cssStyles.permissionsTableHeaderCss
|
||||
}
|
||||
})
|
||||
.component();
|
||||
sectionHeaderContainer.addItem(defaultSectionHeader, { flex: '0 0 auto' });
|
||||
this.defaultSectionComponents.push(defaultSectionHeader);
|
||||
// Delete - just used as a spacer
|
||||
const deleteSectionHeader = modelBuilder.text().component();
|
||||
sectionHeaderContainer.addItem(deleteSectionHeader, { CSSStyles: { 'width': `${permissionsDeleteColumnWidth}px`, 'min-width': `${permissionsDeleteColumnWidth}px` } });
|
||||
|
||||
// Table headers
|
||||
const headerRowContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
|
||||
const ownersCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: nameColumnText }).component();
|
||||
headerRowContainer.addItem(ownersCell, { flex: '1 1 auto', CSSStyles: { 'width': `${permissionsNameColumnWidth}px`, 'min-width': `${permissionsNameColumnWidth}px`, ...cssStyles.tableHeaderCss } });
|
||||
if (includeSticky) {
|
||||
const stickyCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.stickyHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(stickyCell, { CSSStyles: { 'width': `${permissionsStickyColumnWidth}px`, 'min-width': `${permissionsStickyColumnWidth}px` } });
|
||||
rowsContainer.addItem(sectionHeaderContainer);
|
||||
|
||||
// Table headers
|
||||
const headerRowContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
|
||||
|
||||
// Icon (spacer, no text)
|
||||
const typeIconCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ CSSStyles: { 'width': `${permissionsTypeIconColumnWidth}px`, 'min-width': `${permissionsTypeIconColumnWidth}px` } }).component();
|
||||
headerRowContainer.addItem(typeIconCell, { flex: '0 0 auto' });
|
||||
|
||||
// Name
|
||||
const nameCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: nameColumnText }).component();
|
||||
headerRowContainer.addItem(nameCell, { flex: '1 1 auto', CSSStyles: { 'width': `${permissionsNameColumnWidth}px`, 'min-width': `${permissionsNameColumnWidth}px`, ...cssStyles.tableHeaderCss } });
|
||||
if (includeSticky) {
|
||||
const stickyCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.stickyHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(stickyCell, { CSSStyles: { 'width': `${permissionsStickyColumnWidth}px`, 'min-width': `${permissionsStickyColumnWidth}px` } });
|
||||
}
|
||||
|
||||
// Access Permissions Group
|
||||
const accessReadCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.readHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(accessReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px` } });
|
||||
const accessWriteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.writeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(accessWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px` } });
|
||||
const accessExecuteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.executeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(accessExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px`, 'margin-right': '5px' } });
|
||||
// Default Permissions Group
|
||||
const defaultPermissionsHeadersContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
|
||||
const defaultReadCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.readHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
defaultPermissionsHeadersContainer.addItem(defaultReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px` } });
|
||||
const defaultWriteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.writeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
defaultPermissionsHeadersContainer.addItem(defaultWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px` } });
|
||||
const defaultExecuteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.executeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
defaultPermissionsHeadersContainer.addItem(defaultExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px` } });
|
||||
headerRowContainer.addItem(defaultPermissionsHeadersContainer, { flex: '0 0 auto' });
|
||||
this.defaultSectionComponents.push(defaultPermissionsHeadersContainer);
|
||||
|
||||
// Delete
|
||||
const deleteCell = modelBuilder.text().component();
|
||||
headerRowContainer.addItem(deleteCell, { CSSStyles: { 'width': `${permissionsDeleteColumnWidth}px`, 'min-width': `${permissionsDeleteColumnWidth}px` } });
|
||||
|
||||
rowsContainer.addItem(headerRowContainer);
|
||||
|
||||
return rowsContainer;
|
||||
}
|
||||
|
||||
// Access Permissions Group
|
||||
const accessReadCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.readHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(accessReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px` } });
|
||||
const accessWriteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.writeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(accessWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px` } });
|
||||
const accessExecuteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.executeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(accessExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px`, 'margin-right': '5px' } });
|
||||
// Default Permissions Group
|
||||
const defaultReadCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.readHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(defaultReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px` } });
|
||||
const defaultWriteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.writeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(defaultWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px` } });
|
||||
const defaultExecuteCell = modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.executeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component();
|
||||
headerRowContainer.addItem(defaultExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px` } });
|
||||
// Delete
|
||||
const deleteCell = modelBuilder.text().component();
|
||||
headerRowContainer.addItem(deleteCell, { CSSStyles: { 'width': `${permissionsDeleteColumnWidth}px`, 'min-width': `${permissionsDeleteColumnWidth}px` } });
|
||||
|
||||
rowsContainer.addItem(headerRowContainer);
|
||||
|
||||
return rowsContainer;
|
||||
}
|
||||
|
||||
function createCheckbox(builder: azdata.ModelBuilder, checked: boolean, containerWidth: number, containerHeight: number): { container: azdata.FlexContainer, checkbox: azdata.CheckBoxComponent } {
|
||||
@@ -388,7 +446,6 @@ function createCheckbox(builder: azdata.ModelBuilder, checked: boolean, containe
|
||||
.withProperties<azdata.CheckBoxProperties>({ checked: checked, height: 20, width: 20 }).component();
|
||||
const container = builder.flexContainer()
|
||||
.withLayout({ width: containerWidth, height: containerHeight })
|
||||
//.withItems([checkbox], { CSSStyles: { ...cssStyles.permissionCheckboxCss }})
|
||||
.component();
|
||||
container.addItem(checkbox, { CSSStyles: { ...cssStyles.permissionCheckboxCss } });
|
||||
return {
|
||||
|
||||
@@ -11,9 +11,10 @@ import * as through from 'through2';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as auth from '../util/auth';
|
||||
import { IHdfsOptions, IRequestParams } from '../objectExplorerNodeProvider/fileSources';
|
||||
import { IAclStatus, AclEntry, parseAcl, AclPermissionType, parseAclPermissionFromOctal, AclEntryScope } from './aclEntry';
|
||||
import { PermissionStatus, AclEntry, parseAcl, PermissionType, parseAclPermissionFromOctal, AclEntryScope } from './aclEntry';
|
||||
import { Mount } from './mount';
|
||||
import { everyoneName } from '../localizedConstants';
|
||||
import { FileStatus, parseHdfsFileType } from './fileStatus';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const ErrorMessageInvalidDataStructure = localize('webhdfs.invalidDataStructure', "Invalid Data Structure");
|
||||
@@ -75,7 +76,7 @@ export class WebHDFS {
|
||||
endpoint.pathname = this._opts.path + path;
|
||||
let searchOpts = Object.assign(
|
||||
{ 'op': operation },
|
||||
this._opts.user ? { 'user.name': this._opts.user } : {},
|
||||
// this._opts.user ? { 'user.name': this._opts.user } : {},
|
||||
params || {}
|
||||
);
|
||||
endpoint.search = querystring.stringify(searchOpts);
|
||||
@@ -420,13 +421,42 @@ export class WebHDFS {
|
||||
});
|
||||
}
|
||||
|
||||
public getFileStatus(path: string, callback: (error: HdfsError, fileStatus: FileStatus) => void): void {
|
||||
this.checkArgDefined('path', path);
|
||||
|
||||
let endpoint = this.getOperationEndpoint('getfilestatus', path);
|
||||
this.sendRequest('GET', endpoint, undefined, (error, response) => {
|
||||
if (!callback) { return; }
|
||||
if (error) {
|
||||
callback(error, undefined);
|
||||
} else if (response.body.hasOwnProperty('FileStatus')) {
|
||||
const fileStatus = new FileStatus(
|
||||
response.body.FileStatus.accessTime || '',
|
||||
response.body.FileStatus.blockSize || '',
|
||||
response.body.FileStatus.group || '',
|
||||
response.body.FileStatus.length || '',
|
||||
response.body.FileStatus.modificationTime || '',
|
||||
response.body.FileStatus.owner || '',
|
||||
response.body.FileStatus.pathSuffix || '',
|
||||
response.body.FileStatus.permission || '',
|
||||
response.body.FileStatus.replication || '',
|
||||
response.body.FileStatus.snapshotEnabled || '',
|
||||
parseHdfsFileType(response.body.FileStatus.type || 'undefined')
|
||||
);
|
||||
callback(undefined, fileStatus);
|
||||
} else {
|
||||
callback(new HdfsError(ErrorMessageInvalidDataStructure), undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ACL status for given path
|
||||
* @param path The path to the file/folder to get the status of
|
||||
* @param callback Callback to handle the response
|
||||
* @returns void
|
||||
*/
|
||||
public getAclStatus(path: string, callback: (error: HdfsError, aclStatus: IAclStatus) => void): void {
|
||||
public getAclStatus(path: string, callback: (error: HdfsError, permissionStatus: PermissionStatus) => void): void {
|
||||
this.checkArgDefined('path', path);
|
||||
|
||||
let endpoint = this.getOperationEndpoint('getaclstatus', path);
|
||||
@@ -436,14 +466,17 @@ export class WebHDFS {
|
||||
callback(error, undefined);
|
||||
} else if (response.body.hasOwnProperty('AclStatus')) {
|
||||
const permissions = parseAclPermissionFromOctal(response.body.AclStatus.permission);
|
||||
const aclStatus: IAclStatus = {
|
||||
owner: new AclEntry(AclEntryScope.access, AclPermissionType.owner, '', response.body.AclStatus.owner || '', permissions.owner),
|
||||
group: new AclEntry(AclEntryScope.access, AclPermissionType.group, '', response.body.AclStatus.group || '', permissions.group),
|
||||
other: new AclEntry(AclEntryScope.access, AclPermissionType.other, '', everyoneName, permissions.other),
|
||||
stickyBit: !!response.body.AclStatus.stickyBit,
|
||||
entries: (<any[]>response.body.AclStatus.entries).map(entry => parseAcl(entry)).reduce((acc, parsedEntries) => acc.concat(parsedEntries), [])
|
||||
};
|
||||
callback(undefined, aclStatus);
|
||||
const permissionStatus = new PermissionStatus(
|
||||
new AclEntry(AclEntryScope.access, PermissionType.owner, '', response.body.AclStatus.owner || '', permissions.owner),
|
||||
new AclEntry(AclEntryScope.access, PermissionType.group, '', response.body.AclStatus.group || '', permissions.group),
|
||||
new AclEntry(AclEntryScope.access, PermissionType.other, '', everyoneName, permissions.other),
|
||||
!!response.body.AclStatus.stickyBit,
|
||||
// We filter out empty names here since those are already added by the permission bits - WebHDFS creates an extra ACL
|
||||
// entry for the owning group whenever another ACL is added.
|
||||
(<any[]>response.body.AclStatus.entries).map(entry => parseAcl(entry))
|
||||
.reduce((acc, parsedEntries) => acc.concat(parsedEntries), [])
|
||||
.filter(e => e.name !== ''));
|
||||
callback(undefined, permissionStatus);
|
||||
} else {
|
||||
callback(new HdfsError(ErrorMessageInvalidDataStructure), undefined);
|
||||
}
|
||||
@@ -473,6 +506,20 @@ export class WebHDFS {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the permission octal (sticky, owner, group & other) for a file/folder
|
||||
* @param path The path to the file/folder to set the permission of
|
||||
* @param permissionStatus The status containing the permission to set
|
||||
*/
|
||||
public setPermission(path: string, permissionStatus: PermissionStatus, callback: (error: HdfsError) => void): void {
|
||||
this.checkArgDefined('path', path);
|
||||
this.checkArgDefined('permissionStatus', permissionStatus);
|
||||
let endpoint = this.getOperationEndpoint('setpermission', path, { permission: permissionStatus.permissionOctal });
|
||||
this.sendRequest('PUT', endpoint, undefined, (error) => {
|
||||
return callback && callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all mounts for a HDFS connection
|
||||
* @param callback Callback to handle the response
|
||||
|
||||
Reference in New Issue
Block a user