f72102f27f8bf09883b930b2eeed054d4f5f6735
4 * This renders the administrative view for an account,
5 * allowing for adding profile URIs, custom scope bundles,
6 * and management of issued tokens.
9 const th
= require('./template-helper');
10 const { sessionNavLinks
} = require('@squeep/authentication-module');
15 * @param {string} profile profile
16 * @returns {string} li
18 function renderProfileLI(profile
) {
19 return `\t<li><a class="uri" id="${profile}">${profile}</a></li>`;
25 * @param {string} profile profile
26 * @param {string} scope scope
27 * @param {boolean} selected is selected
28 * @returns {string} td
30 function renderProfileScopeIndicator(profile
, scope
, selected
) {
31 const checked
= selected
? ' checked' : '';
33 \t\t\t<input type="checkbox" id="${profile}-${scope}" name="scopes-${profile}[]" value="${scope}"${checked}>
39 * @param {string} scope scope
40 * @param {object} details details
41 * @param {string[]} profiles profiles
42 * @returns {string} tr
44 function renderScopeRow(scope
, details
, profiles
) {
45 return `\t<tr class="scope">
46 ${(profiles || []).map((profile) => renderProfileScopeIndicator(profile, scope, details.profiles.includes(profile))).join('\n')}
47 \t\t<th scope="row"><label>${scope}</label></th>
48 \t\t<td class="description">${details.description}</td>
49 \t\t<td>${details.application}</td>
50 \t\t<td class="scope-actions">` +
51 (details
.isManuallyAdded
? `
52 \t\t\t<button type="submit" name="action" value="delete-scope-${encodeURIComponent(scope)}">Delete</button>
61 * @param {string} profile profile
62 * @returns {string} th
64 function renderProfileHeader(profile
) {
65 return `<th scope="col" class="vertical uri">
73 * @param {object} scopeIndex scopes
74 * @param {string[]} profiles profiles
75 * @returns {string} table
77 function scopeIndexTable(scopeIndex
, profiles
) {
81 ${(profiles || []).map((profile) => renderProfileHeader(profile)).join('\n')}
82 \t\t<th scope="col">Scope</th>
83 \t\t<th scope="col">Description</th>
84 \t\t<th scope="col">Application</th>
85 \t\t<th scope="col" class="scope-actions"></th>
89 ${Object.entries(scopeIndex).sort(th.scopeCompare).map(([scope, details]) => renderScopeRow(scope, details, profiles)).join('\n')}
96 * @param {object} token token
97 * @returns {string} type
99 function _tokenType(token
) {
100 if (token
.resource
) {
101 return 'ticket-token';
103 if (!token
.isToken
) {
111 * @param {object} token token
112 * @returns {string} tr
114 function renderTokenRow(token
) {
115 const createdTitle
= token
.refreshed
? 'Refreshed At' : 'Created At';
116 const createdDate
= token
.refreshed
? token
.refreshed : token
.created
;
118 <td>${_tokenType(token)}</td>
119 \t\t\t<td class="uri">${token.clientId}</td>
120 \t\t\t<td class="uri">${token.profile}</td>
121 <td class="scope">${(token.scopes || []).join(', ')}</td>
122 \t\t\t<td class="code">${token.codeId}</td>
123 \t\t\t<td>${th.timeElement(createdDate, { title: createdTitle })}</td>
124 \t\t\t<td>${th.timeElement(token.expires, { title: 'Expires At' })}</td>
125 \t\t\t<td>${token.isRevoked}</td>
126 <td>${token.resource ? token.resource : ''}</td>
128 token
.isRevoked
? '' : `
129 \t\t\t\t<button type="submit" name="action" value="revoke-${token.codeId}">Revoke</button>`) + `
135 * @returns {string} tr
137 function noTokensRows() {
139 \t\t\t<td colspan="10" class="centered">(No active or recent tokens.)</td>
145 * @param {object} tokens tokens
146 * @returns {string} table
148 function tokenTable(tokens
) {
149 const tokenRows
= tokens
?.length
? tokens
.map((token
) => renderTokenRow(token
)) : noTokensRows();
150 const formOpen
= tokens
?.length
? '<form method="POST">\n' : '';
151 const formClose
= tokens
?.length
? '\n</form>' : '';
152 return `${formOpen}<table>
155 \t\t\t<th scope="col">Type</th>
156 \t\t\t<th scope="col">Client Identifier / Ticket Subject</th>
157 \t\t\t<th scope="col">Profile</th>
158 \t\t\t<th scope="col">Scopes</th>
159 \t\t\t<th scope="col">Code</th>
160 \t\t\t<th scope="col">Created or Refreshed</th>
161 \t\t\t<th scope="col">Expires</th>
162 \t\t\t<th scope="col">Revoked</th>
163 \t\t\t<th scope="col">Resource</th>
164 \t\t\t<th scope="col"></th>
168 ${tokenRows.join('\n')}
170 </table>${formClose}`;
175 * @param {object} ctx context
176 * @returns {string} section
178 function mainContent(ctx
) {
179 const profileList
= (ctx
.profilesScopes
?.profiles
|| []).map((p
) => renderProfileLI(p
)).join('\n');
185 \t<form method="POST">
187 \t\t\t<legend>Add New Profile</legend>
189 \t\t\t\tThe profile identity URIs associated with this account.
190 \t\t\t\tEach must indicate this service as the authorization endpoint.
193 \t\t\t<label for="profile">Profile URL:</label>
194 \t\t\t<input type="url" id="profile" name="profile" size="96">
195 \t\t\t<button type="submit" name="action" value="new-profile">Add Profile</button>
203 \t\t\t\tScopes Associated with Profiles for Convenience
205 \t\t<form method="POST">
207 \t\t\t\t<legend>Manage Additional Profile Scope Availability</legend>
209 \t\t\t\t\tThis table lists pre-defined scopes which you can choose to add to any authorization request, whether the client requested them or not.
210 \t\t\t\t\tSelecting one for a profile makes it conveniently available for quick inclusion when authorizing a client request.
211 \t\t\t\t\tAny scope not in this table or not selected for a profile can always be added in the ad hoc field on the authorization request.
214 ${scopeIndexTable(ctx.profilesScopes.scopeIndex, ctx.profilesScopes.profiles)}
215 \t\t\t\t<button type="submit" name="action" value="save-scopes">Save</button>
219 \t\t<form method="POST">
221 \t\t\t\t<legend>Add New Scope</legend>
222 \t\t\t\t<label for="scope">Scope:</label>
223 \t\t\t\t<input type="text" id="scope" name="scope">
224 \t\t\t\t<label for="description">Description:</label>
225 \t\t\t\t<input type="text" id="description" name="description">
226 \t\t\t\t<label for="application">Application:</label>
227 \t\t\t\t<input type="text" id="application" name="application">
228 \t\t\t\t<button type="submit" name="action" value="new-scope">Add Scope</button>
235 ${tokenTable(ctx.tokens)}
242 * @param {object} ctx context
243 * @param {object} ctx.profilesScopes.scopeIndex scopes
244 * @param {string[]} ctx.profilesScopes.profiles profiles
245 * @param {object[]} ctx.tokens tokens
246 * @param {object} options options
247 * @param {object} options.manager manager options
248 * @param {string} options.manager.pageTitle page title
249 * @param {string} options.manager.logoUrl logo url
250 * @param {string[]} options.manager.footerEntries footer entries
251 * @returns {string} page
253 module
.exports
= (ctx
, options
) => {
254 const pagePathLevel
= 1;
255 const htmlOptions
= {
256 pageIdentifier: 'admin',
257 pageTitle: options
.manager
.pageTitle
+ ' - Admin',
258 logoUrl: options
.manager
.logoUrl
,
259 footerEntries: options
.manager
.footerEntries
,
261 th
.navLinks(pagePathLevel
, ctx
, htmlOptions
);
262 sessionNavLinks(1, ctx
, htmlOptions
);
266 return th
.htmlPage(1, ctx
, htmlOptions
, content
);