-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathRunAsWebviewProvider.ts
150 lines (134 loc) · 5.48 KB
/
RunAsWebviewProvider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { Client, errors, query, values } from 'faunadb';
import * as vscode from 'vscode';
export default class RunAsWebviewProvider
implements vscode.WebviewViewProvider {
private _view?: vscode.WebviewView;
public role: string | null = null;
constructor(
private readonly _extensionUri: vscode.Uri,
private client: Client,
private secret: string
) {}
public getSecretWithRole() {
return this.role ? [this.secret, this.role].join(':') : undefined;
}
public async resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken
) {
this._view = webviewView;
webviewView.webview.options = {
// Allow scripts in the webview
enableScripts: true,
localResourceRoots: [this._extensionUri]
};
const isAdmin = await this.isAdmin();
if (isAdmin) {
webviewView.webview.html = await this.getHtmlForRunAs();
} else {
webviewView.webview.html = this.getHtmlForNonAdmin();
}
webviewView.webview.onDidReceiveMessage(data => {
switch (data.type) {
case 'roleChanged':
this.role = data.role;
break;
case 'deactivateRunAs':
this.role = null;
break;
case 'collectionChanged':
this.role = [this.role, data.collection].join('/');
break;
case 'idChanged':
this.role = [this.role, data.id].join('/');
}
});
}
private async isAdmin() {
return this.client
.query(query.Now(), { secret: this.secret + ':admin' })
.then(() => true)
.catch(err => false);
}
private getHtmlForNonAdmin() {
return `
<div>
${this.getHtmlForDesc()}
<span style="color: red">Available only for secret with 'admin' role</span>
</div>
`;
}
private async getRoles() {
const result = await vscode.commands.executeCommand<{
error?: errors.FaunaHTTPError;
data?: values.Ref[];
}>('fauna.query', query.Paginate(query.Roles()));
if (result?.error) {
vscode.window.showErrorMessage(result!.error.requestResult.responseRaw);
return;
}
return result?.data ?? [];
}
private async getHtmlForRunAs() {
const scriptUri = this._view!.webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, 'media', 'runAs.js')
);
const nonce = getNonce();
const roles = await this.getRoles();
return `
<html>
<head></head>
<body>
<div style="margin-top: 10px">
${this.getHtmlForDesc()}
<div id="runAsActivate" style="display: flex; align-items: center; justify-content: center; height: 36px; width: 50%; color: white; cursor: pointer; background: #3f00a5;">
<svg width="20" aria-hidden="true" data-prefix="fas" data-icon="user-lock" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" data-fa-i2svg=""><path fill="currentColor" d="M320 320c0-11.1 3.1-21.4 8.1-30.5-4.8-.5-9.5-1.5-14.5-1.5h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h280.9c-5.5-9.5-8.9-20.3-8.9-32V320zm-96-64c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm384 32h-32v-48c0-44.2-35.8-80-80-80s-80 35.8-80 80v48h-32c-17.7 0-32 14.3-32 32v160c0 17.7 14.3 32 32 32h224c17.7 0 32-14.3 32-32V320c0-17.7-14.3-32-32-32zm-80 0h-64v-48c0-17.6 14.4-32 32-32s32 14.4 32 32v48z"></path></svg>
<span style="margin-left: 5px">Select role</span>
</div>
</div>
<div id="runAs" style="display: none; max-width: 330px">
<div style="display: flex; align-items:center">
<select id="roleId" name="roleId" style="height: 36px; width: 50%">
<option value="@doc">Specify a document</option>
<option selected value="admin">Admin</option>
<option value="server">Server</option>
${roles!.map(
role =>
`<option value="@role/${role.id}">
${role.id}
</option>`
)}
</select>
<button id="closeRunAs" style="margin-left: 10px; height: 35px; width: 50%; border:none;">CANCEL</button>
</div>
<div style="display: none; margin-top: 10px" id="specifyDocument">
<input style="width: 50%; box-sizing:border-box; height: 36px" id="collectionInput" name="collection" placeholder="Collection" />
<input style="width: 50%; box-sizing:border-box; height: 36px; margin-left: 10px;" id="idInput" name="id" placeholder="ID" />
</div>
</div>
<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
private getHtmlForDesc() {
return `
<div style="margin-bottom: 20px">
Verify your <a href="https://docs.fauna.com/fauna/current/security/abac">ABAC</a>
configuration by running your query with an Admin or Server
<a href="https://docs.fauna.com/fauna/current/security/keys#scoped-keys">key</a>,
a specific <a href="https://docs.fauna.com/fauna/current/security/roles">Role</a>, or a specific identity document. See
<a href="https://docs.fauna.com/fauna/current/security/">Fauna Security</a> for details.
</div>
`;
}
}
function getNonce() {
let text = '';
const possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}