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

Fine grained access control #322

Merged
merged 27 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8528648
Initial working changes
bosschaert Dec 16, 2024
860d900
Use X-da-actions header
bosschaert Dec 16, 2024
56f55cc
Render directories with no access as empty ones
bosschaert Dec 17, 2024
ad0e0a4
Handle the case where paste fails because of ACL
bosschaert Dec 17, 2024
4961ce6
Updating scopes for org info
auniverseaway Dec 18, 2024
2d5fd95
Merge branch 'main' into acl
bosschaert Dec 19, 2024
265aa8b
Merge remote-tracking branch 'origin/acl' into acl
bosschaert Dec 23, 2024
1d12837
Merge branch 'main' into acl
bosschaert Jan 7, 2025
ac9d015
Fix ommissions in Playwright tests for local running
bosschaert Jan 7, 2025
b7c1923
Log any config save errors
bosschaert Jan 10, 2025
d45af0a
ACL - Disable new in browse view
auniverseaway Jan 12, 2025
c1f374b
ACLs - Actionbar
auniverseaway Jan 12, 2025
6e4cbb1
ACL - Editor w/ performance improvements
auniverseaway Jan 13, 2025
1401e77
Attempt giving the websocket more bandwidth
auniverseaway Jan 13, 2025
328800e
ACL - More timing tweaks
auniverseaway Jan 13, 2025
7c8dfe6
More performance - Bump prose init up in call stack
auniverseaway Jan 13, 2025
b439523
ACL - Sheet UI
auniverseaway Jan 14, 2025
83d5f3e
ACL - Fix sheet bugs... 404, etc.
auniverseaway Jan 14, 2025
b461386
Merge branch 'main' into acl
auniverseaway Jan 19, 2025
62abfb8
Merge branch 'main' into acl
auniverseaway Jan 19, 2025
3642f58
Fix tests
auniverseaway Jan 19, 2025
aa5c2d8
ACL - Disable restore button
auniverseaway Jan 19, 2025
4bd29f9
Merge branch 'acl' of github.com:adobe/da-live into acl
auniverseaway Jan 19, 2025
d206f93
Merge branch 'main' into acl
auniverseaway Feb 2, 2025
0cfca3c
WIP - Backwards compatibility
auniverseaway Feb 2, 2025
30dbe51
Support 403. Cache previous list permissions.
auniverseaway Feb 3, 2025
1e1e60b
Minor test fixes. Add proper 404
auniverseaway Feb 3, 2025
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
32 changes: 31 additions & 1 deletion 404.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,41 @@
<meta name="404" content="local"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script src="/scripts/scripts.js" type="module"></script>
<style>body { display: none; }</style>
<link rel="icon" href="data:,">
</head>
<body>
<header></header>
<main><div></div></main>
<main>
<div>
<div class="nx-404">
<div>
<div>
<picture>
<source type="image/webp" srcset="./media_1412370e98b188540f4343de87798c7587b9f4201.jpeg?width=2000&#x26;format=webply&#x26;optimize=medium" media="(min-width: 600px)">
<source type="image/webp" srcset="./media_1412370e98b188540f4343de87798c7587b9f4201.jpeg?width=750&#x26;format=webply&#x26;optimize=medium">
<source type="image/jpeg" srcset="./media_1412370e98b188540f4343de87798c7587b9f4201.jpeg?width=2000&#x26;format=jpeg&#x26;optimize=medium" media="(min-width: 600px)">
<img loading="lazy" alt="" src="./media_1412370e98b188540f4343de87798c7587b9f4201.jpeg?width=750&#x26;format=jpeg&#x26;optimize=medium" width="2000" height="1333">
</picture>
</div>
</div>
<div>
<div>
<h1 id="not-found">404 Not Found</h1>
<h2 id="a-new-adventure-is-calling">A new adventure is calling.</h2>
<p>
<picture>
<source type="image/webp" srcset="./media_1f9d87b6de1c40d8dde6fa083033c72e76178a81b.png?width=2000&#x26;format=webply&#x26;optimize=medium" media="(min-width: 600px)">
<source type="image/webp" srcset="./media_1f9d87b6de1c40d8dde6fa083033c72e76178a81b.png?width=750&#x26;format=webply&#x26;optimize=medium">
<source type="image/png" srcset="./media_1f9d87b6de1c40d8dde6fa083033c72e76178a81b.png?width=2000&#x26;format=png&#x26;optimize=medium" media="(min-width: 600px)">
<img loading="lazy" alt="" src="./media_1f9d87b6de1c40d8dde6fa083033c72e76178a81b.png?width=750&#x26;format=png&#x26;optimize=medium" width="400" height="150">
</picture>
</p>
</div>
</div>
</div>
</div>
</main>
<footer></footer>
</body>
</html>
41 changes: 24 additions & 17 deletions blocks/browse/da-actionbar/da-actionbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const STYLE = await getStyle(import.meta.url);
export default class DaActionBar extends LitElement {
static properties = {
items: { attribute: false },
_canPaste: { state: true },
permissions: { attribute: false },
_isCopying: { state: true },
_isDeleting: { state: true },
_isMoving: { state: true },
currentPath: { type: String },
Expand All @@ -29,7 +30,7 @@ export default class DaActionBar extends LitElement {
if (props.has('items')) {
// Reset state when items go empty
if (this.items.length === 0) {
this._canPaste = false;
this._isCopying = false;
this._isMoving = false;
this._isDeleting = false;
}
Expand All @@ -39,7 +40,7 @@ export default class DaActionBar extends LitElement {
}

handleClear() {
this._canPaste = false;
this._isCopying = false;
this._isMoving = false;
this._isDeleting = false;
const opts = { detail: true, bubbles: true, composed: true };
Expand All @@ -54,12 +55,12 @@ export default class DaActionBar extends LitElement {
}

handleCopy() {
this._canPaste = true;
this._isCopying = true;
}

handleMove() {
this._isCopying = true;
this._isMoving = true;
this._canPaste = true;
}

handlePaste() {
Expand Down Expand Up @@ -106,13 +107,19 @@ export default class DaActionBar extends LitElement {
return itemDir !== this.currentPath;
}

get _canWrite() {
if (!this.permissions) return false;
return this.permissions.some((permission) => permission === 'write');
}

get _canShare() {
return this.items.some((item) => item.ext && item.ext !== 'link');
const isFile = this.items.some((item) => item.ext && item.ext !== 'link');
return isFile && !this._isCopying;
}

get currentAction() {
const itemStr = this.items.length > 1 ? 'items' : 'item';
if (this._canPaste) {
if (this._isCopying && this._canWrite) {
const folderName = this.currentPath.split('/').pop();
return `Paste ${this.items.length} ${itemStr} into ${folderName}`;
}
Expand All @@ -135,37 +142,37 @@ export default class DaActionBar extends LitElement {
<div class="da-action-bar-right-rail">
<button
@click=${this.handleRename}
class="rename-button ${this.items.length === 1 ? '' : 'hide'} ${this._canPaste ? 'hide' : ''}">
class="rename-button ${this._canWrite ? '' : 'hide'} ${this.items.length === 1 ? '' : 'hide'} ${this._isCopying ? 'hide' : ''}">
<img src="/blocks/browse/da-browse/img/Smock_TextEdit_18_N.svg" />
<span>Rename</span>
</button>
<button
@click=${this.handleCopy}
class="copy-button ${this._canPaste ? 'hide' : ''}">
class="copy-button ${this._isCopying ? 'hide' : ''}">
<img src="/blocks/browse/da-browse/img/Smock_Copy_18_N.svg" />
<span>Copy</span>
</button>
<button
@click=${this.handleMove}
class="copy-button ${this._canPaste ? 'hide' : ''}">
<img src="/blocks/browse/da-browse/img/Smock_Cut_18_N.svg" />
<span>Cut</span>
</button>
@click=${this.handleMove}
class="copy-button ${this._canWrite ? '' : 'hide'} ${this._isCopying ? 'hide' : ''}">
<img src="/blocks/browse/da-browse/img/Smock_Cut_18_N.svg" />
<span>Cut</span>
</button>
<button
@click=${this.handlePaste}
class="copy-button ${this._canPaste ? '' : 'hide'}">
class="copy-button ${this._canWrite ? '' : 'hide'} ${this._isCopying ? '' : 'hide'}">
<img src="/blocks/browse/da-browse/img/Smock_Copy_18_N.svg" />
<span>Paste</span>
</button>
<button
@click=${this.handleDelete}
class="delete-button ${this._canPaste ? 'hide' : ''}">
class="delete-button ${this._canWrite ? '' : 'hide'} ${this._isCopying ? 'hide' : ''}">
<img src="/blocks/browse/da-browse/img/Smock_Delete_18_N.svg" />
<span>Delete</span>
</button>
<button
@click=${this.handleShare}
class="share-button ${this._canShare && !this._canPaste ? '' : 'hide'}">
class="share-button ${this._canShare ? '' : 'hide'}">
<img src="/blocks/browse/img/Smock_Share_18_N.svg" />
<span>Share</span>
</button>
Expand Down
9 changes: 9 additions & 0 deletions blocks/browse/da-browse/da-browse.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
this.shadowRoot.adoptedStyleSheets = [STYLE];
}

handlePermissions(e) {
if (this.newCmp) this.newCmp.permissions = e.detail;
}

async update(props) {
if (props.has('details') && this.details) {
// Only re-fetch if the orgs are different
Expand Down Expand Up @@ -77,7 +81,7 @@

// Sort by length in descending order (longest first)
const matchedConf = matchedConfs.sort((a, b) => b.length - a.length)[0];
console.log(matchedConf);

Check warning on line 84 in blocks/browse/da-browse/da-browse.js

View workflow job for this annotation

GitHub Actions / Running tests (20.x)

Unexpected console statement

return matchedConf.split('=')[1];
}
Expand All @@ -98,6 +102,10 @@
return this._tabItems.find((tab) => tab.selected).id;
}

get newCmp() {
return this.shadowRoot.querySelector('da-new');
}

renderNew() {
return html`
<da-new
Expand All @@ -117,6 +125,7 @@
class="da-list-type-${type}"
fullpath="${fullpath}"
editor="${this.editor}"
@onpermissions=${this.handlePermissions}
select="${select ? true : nothing}"
sort="${sort ? true : nothing}"
drag="${drag ? true : nothing}"></da-list>`;
Expand Down
26 changes: 25 additions & 1 deletion blocks/browse/da-list/da-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default class DaList extends LitElement {
drag: { type: Boolean },
listItems: { attribute: false },
newItem: { attribute: false },
_permissions: { state: true },
_listItems: { state: true },
_selectedItems: { state: true },
_dropFiles: { state: true },
Expand Down Expand Up @@ -70,9 +71,18 @@ export default class DaList extends LitElement {
this._status = { type, text, description };
}

handlePermissions(permissions) {
this._permissions = permissions;

// Notify parent
const opts = { detail: permissions, bubbles: true, composed: true };
const event = new CustomEvent('onpermissions', opts);
this.dispatchEvent(event);
}

async getList() {
const resp = await daFetch(`${DA_ORIGIN}/list${this.fullpath}`);
if (!resp.ok) return null;
if (resp.permissions) this.handlePermissions(resp.permissions);
return resp.json();
}

Expand Down Expand Up @@ -128,6 +138,12 @@ export default class DaList extends LitElement {
this.requestUpdate();
}

wait(milliseconds) {
return new Promise((r) => {
setTimeout(r, milliseconds);
});
}

async handlePasteItem(item) {
let continuation = true;
let continuationToken;
Expand All @@ -141,6 +157,13 @@ export default class DaList extends LitElement {
if (resp.status === 204) {
continuation = false;
break;
} else if (resp.status >= 400) {
this.setStatus('Copying', 'There was an issue copying.');

// TODO maybe there is a better way to keep the status dialog visible for a bit?
await this.wait(2000);

return;
}
const json = await resp.json();
({ continuationToken } = json);
Expand Down Expand Up @@ -420,6 +443,7 @@ export default class DaList extends LitElement {
${this.drag ? this.renderDropArea() : nothing}
</div>
<da-actionbar
.permissions=${this._permissions}
@clearselection=${this.handleClear}
@rename=${this.handleRename}
@onpaste=${this.handlePaste}
Expand Down
9 changes: 9 additions & 0 deletions blocks/browse/da-new/da-new.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ button {
transition: outline-offset .2s;
}

.da-actions-new-button:disabled {
color: var(--s2-gray-700);
background-color: transparent;
outline-color: var(--s2-gray-700);
border: 2px solid var(--s2-gray-700);
border-style: dotted;
cursor: not-allowed;
}

.da-actions-create.menu .da-actions-menu {
display: grid;
gap: 6px;
Expand Down
8 changes: 7 additions & 1 deletion blocks/browse/da-new/da-new.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default class DaNew extends LitElement {
static properties = {
fullpath: { type: String },
editor: { type: String },
permissions: { attribute: false },
_createShow: { attribute: false },
_createType: { attribute: false },
_createFile: { attribute: false },
Expand Down Expand Up @@ -127,10 +128,15 @@ export default class DaNew extends LitElement {
this._externalUrl = '';
}

get _disabled() {
if (!this.permissions) return true;
return !this.permissions.some((permission) => permission === 'write');
}

render() {
return html`
<div class="da-actions-create ${this._createShow}">
<button class="da-actions-new-button" @click=${this.handleCreateMenu}>New</button>
<button class="da-actions-new-button" @click=${this.handleCreateMenu} ?disabled=${this._disabled}>New</button>
<ul class="da-actions-menu">
<li class=da-actions-menu-item>
<button data-type=folder @click=${this.handleNewType}>Folder</button>
Expand Down
59 changes: 44 additions & 15 deletions blocks/edit/da-content/da-content.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import { LitElement, html } from 'da-lit';
import { LitElement, html, nothing } from 'da-lit';

import getSheet from '../../shared/sheet.js';
import '../da-editor/da-editor.js';
import '../da-preview/da-preview.js';
import '../da-versions/da-versions.js';

const sheet = await getSheet('/blocks/edit/da-content/da-content.css');

export default class DaContent extends LitElement {
static properties = {
details: { attribute: false },
_sourceUrl: { state: true },
permissions: { attribute: false },
proseEl: { attribute: false },
wsProvider: { attribute: false },
_editorLoaded: { state: true },
_versionUrl: { state: true },
};

connectedCallback() {
super.connectedCallback();
this.shadowRoot.adoptedStyleSheets = [sheet];
this._sourceUrl = this.details.sourceUrl;
}

disconnectWebsocket() {
if (this.wsProvider) {
this.wsProvider.disconnect({ data: 'Client navigation' });
this.wsProvider = undefined;
}
}

update(props) {
super.update(props);
}

showPreview() {
Expand All @@ -31,6 +42,14 @@ export default class DaContent extends LitElement {
this.daVersions.classList.add('show-versions');
}

async handleEditorLoaded() {
if (this._editorLoaded) return;
const preview = import('../da-preview/da-preview.js');
const versions = import('../da-versions/da-versions.js');
await Promise.all([preview, versions]);
this._editorLoaded = true;
}

handleReset() {
this._versionUrl = null;
}
Expand All @@ -52,18 +71,28 @@ export default class DaContent extends LitElement {
render() {
return html`
<div class="editor-wrapper">
<da-editor path="${this._sourceUrl}" version="${this._versionUrl}" @versionreset=${this.handleReset}></da-editor>
<div class="da-editor-tabs">
<div class="da-editor-tabs-full">
<button class="da-editor-tab show-preview" title="Preview" @click=${this.showPreview}>Preview</button>
</div>
<div class="da-editor-tabs-quiet">
<button class="da-editor-tab quiet show-versions" title="Versions" @click=${this.showVersions}>Versions</button>
<da-editor
path="${this.details.sourceUrl}"
version="${this._versionUrl}"
.permissions=${this.permissions}
.proseEl=${this.proseEl}
.wsProvider=${this.wsProvider}
@proseloaded=${this.handleEditorLoaded}
@versionreset=${this.handleReset}>
</da-editor>
${this._editorLoaded ? html`
<div class="da-editor-tabs">
<div class="da-editor-tabs-full">
<button class="da-editor-tab show-preview" title="Preview" @click=${this.showPreview}>Preview</button>
</div>
<div class="da-editor-tabs-quiet">
<button class="da-editor-tab quiet show-versions" title="Versions" @click=${this.showVersions}>Versions</button>
</div>
</div>
</div>
` : nothing}
</div>
<da-preview path=${this.details.previewUrl}></da-preview>
<da-versions path=${this.details.fullpath} @preview=${this.handlePreview} @close=${this.handleCloseVersions}></da-versions>
${this._editorLoaded ? html`<da-preview path=${this.details.previewUrl}></da-preview>` : nothing}
${this._editorLoaded ? html`<da-versions path=${this.details.fullpath} @preview=${this.handlePreview} @close=${this.handleCloseVersions}></da-versions>` : nothing}
`;
}
}
Expand Down
Loading
Loading