Authentication token generation WIP (#98)

First working version.
This commit is contained in:
John Livingston 2024-06-17 14:54:29 +02:00
parent 6bb29d79f8
commit a9b6474b8f
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
7 changed files with 75 additions and 32 deletions

View File

@ -10,6 +10,18 @@
livechat-token-list { livechat-token-list {
table { table {
@include tables.data-table; @include tables.data-table;
width: 100%;
tr th:first-child,
tr th:last-child {
width: 50px;
}
label {
// Reset some styles set by peertube
font-weight: inherit;
}
} }
.livechat-create-token { .livechat-create-token {

View File

@ -116,8 +116,8 @@ declare const LOC_SHARE_CHAT_PEERTUBE_TIPS: string
declare const LOC_SHARE_CHAT_DOCK: string declare const LOC_SHARE_CHAT_DOCK: string
declare const LOC_SHARE_CHAT_DOCK_TIPS: string declare const LOC_SHARE_CHAT_DOCK_TIPS: string
declare const LOC_TOKEN_LABEL: string declare const LOC_TOKEN_LABEL: string
declare const LOC_TOKEN_JID: string
declare const LOC_TOKEN_PASSWORD: string declare const LOC_TOKEN_PASSWORD: string
declare const LOC_TOKEN_ACTION_CREATE: string declare const LOC_TOKEN_ACTION_CREATE: string
declare const LOC_TOKEN_ACTION_REVOKE: string declare const LOC_TOKEN_ACTION_REVOKE: string
declare const LOC_TOKEN_DEFAULT_LABEL: string declare const LOC_TOKEN_DEFAULT_LABEL: string
declare const LOC_TOKEN_ACTION_REVOKE_CONFIRM: string

View File

@ -16,7 +16,6 @@ export function tplTokenList (el: LivechatTokenListElement): TemplateResult {
<tr> <tr>
<th scope="col"></th> <th scope="col"></th>
<th scope="col">${ptTr(LOC_TOKEN_LABEL)}</th> <th scope="col">${ptTr(LOC_TOKEN_LABEL)}</th>
<th scope="col">${ptTr(LOC_TOKEN_JID)}</th>
<th scope="col">${ptTr(LOC_TOKEN_PASSWORD)}</th> <th scope="col">${ptTr(LOC_TOKEN_PASSWORD)}</th>
<th scope="col"></th> <th scope="col"></th>
</tr> </tr>
@ -24,19 +23,29 @@ export function tplTokenList (el: LivechatTokenListElement): TemplateResult {
<tbody> <tbody>
${ ${
repeat(el.tokenList ?? [], (token) => token.id, (token) => { repeat(el.tokenList ?? [], (token) => token.id, (token) => {
html`<tr> return html`<tr>
<td>${ <td>${
el.mode === 'select' el.mode === 'select'
? html`<input ? html`<input
type="radio" type="radio"
?selected=${el.currentSelectedToken?.id === token.id} name="livechat-token"
@click=${el.selectToken} value=${token.id}
id=${`livechat-token-radio-${token.id}`}
?checked=${el.currentSelectedToken?.id === token.id}
@click=${(ev: Event) => el.selectToken(ev, token)}
/>` />`
: '' : ''
}</td> }</td>
<td>${token.label}</td> <td>
<td>${token.jid}</td> <label for=${`livechat-token-radio-${token.id}`}>
<td>${token.password}</td> ${token.label}
</label>
</td>
<td>
<label for=${`livechat-token-radio-${token.id}`}>
${token.password}
</label>
</td>
<td> <td>
<button type="button" <button type="button"
class="livechat-revoke-token" class="livechat-revoke-token"

View File

@ -42,6 +42,10 @@ export class LivechatTokenListElement extends LivechatElement {
return new Task(this, { return new Task(this, {
task: async () => { task: async () => {
this.tokenList = await this._tokenListService.fetchTokenList() this.tokenList = await this._tokenListService.fetchTokenList()
if (this.mode === 'select' && this.tokenList.length) {
this.currentSelectedToken = this.tokenList[0]
this.dispatchEvent(new CustomEvent('update', {}))
}
this.actionDisabled = false this.actionDisabled = false
}, },
args: () => [] args: () => []
@ -57,13 +61,17 @@ export class LivechatTokenListElement extends LivechatElement {
} }
public selectToken (ev: Event, token: LivechatToken): void { public selectToken (ev: Event, token: LivechatToken): void {
ev.preventDefault()
if (!this.tokenList?.includes(token)) { return } if (!this.tokenList?.includes(token)) { return }
this.currentSelectedToken = token this.currentSelectedToken = token
this.requestUpdate('tokenList')
this.dispatchEvent(new CustomEvent('update', {})) this.dispatchEvent(new CustomEvent('update', {}))
} }
public async revokeToken (token: LivechatToken): Promise<void> { public async revokeToken (token: LivechatToken): Promise<void> {
const confirmMsg = await this.ptTranslate(LOC_TOKEN_ACTION_REVOKE_CONFIRM)
// Note: we can't use peertube showModal to confirm if we already are in a modal...
if (!window.confirm(confirmMsg)) { return }
this.actionDisabled = true this.actionDisabled = true
try { try {
await this._tokenListService.revokeToken(token) await this._tokenListService.revokeToken(token)

View File

@ -4,6 +4,7 @@
import type { Video } from '@peertube/peertube-types' import type { Video } from '@peertube/peertube-types'
import type { LiveChatSettings } from '../../lib/contexts/peertube' import type { LiveChatSettings } from '../../lib/contexts/peertube'
import type { LivechatTokenListElement } from '../../lib/elements/token-list'
import { html, PropertyValues, TemplateResult } from 'lit' import { html, PropertyValues, TemplateResult } from 'lit'
import { customElement, property } from 'lit/decorators.js' import { customElement, property } from 'lit/decorators.js'
import { LivechatElement } from '../../lib/elements/livechat' import { LivechatElement } from '../../lib/elements/livechat'
@ -222,29 +223,37 @@ export class ShareChatElement extends LivechatElement {
} }
protected _computeUrlDock (): ComputedUrl { protected _computeUrlDock (): ComputedUrl {
return { const tokenList: LivechatTokenListElement | null = this.querySelector('livechat-token-list')
shareString: '', const token = tokenList?.currentSelectedToken
openUrl: undefined if (!token) {
return {
shareString: '',
openUrl: undefined
}
}
const uriOptions: UriOptions = {
ignoreAutoColors: true,
permanent: true
} }
// const uriOptions: UriOptions = {
// ignoreAutoColors: true,
// permanent: true
// }
// // Note: for the "embed" case, the url is always the same as the iframe. let url = getIframeUri(this.ptContext.ptOptions, this._settings, this._video, uriOptions)
// // So we use getIframeUri to compte, and just change the finale result if we really want the iframe. if (!url) {
// const url = getIframeUri(this.ptContext.ptOptions, this._settings, this._video, uriOptions) return {
// if (!url) { shareString: '',
// return { openUrl: undefined
// shareString: '', }
// openUrl: undefined }
// }
// }
// return { url += '#?p=' + encodeURIComponent(token.password)
// shareString: url, url += '&j=' + encodeURIComponent(token.jid)
// openUrl: url if (token.nickname) {
// } url += '&n=' + encodeURIComponent(token.nickname)
}
return {
shareString: url,
openUrl: url
}
} }
protected _computeUrlEmbed (): ComputedUrl { protected _computeUrlEmbed (): ComputedUrl {

View File

@ -167,8 +167,13 @@ function _tplShareChatEmbedOptions (el: ShareChatElement): TemplateResult {
` `
} }
function _tplShareChatDockOptions (_el: ShareChatElement): TemplateResult { function _tplShareChatDockOptions (el: ShareChatElement): TemplateResult {
return html`<livechat-token-list mode="select"></livechat-token-list>` return html`<livechat-token-list
mode="select"
@update=${(_e: CustomEvent) => {
el.requestUpdate()
}}
></livechat-token-list>`
} }
function _tplShareChatXMPPOptions (_el: ShareChatElement): TemplateResult { function _tplShareChatXMPPOptions (_el: ShareChatElement): TemplateResult {

View File

@ -531,8 +531,8 @@ share_chat_dock_tips: |
Please note that these tokens have no expiration date. Please note that these tokens have no expiration date.
token_label: Label token_label: Label
token_jid: Username
token_password: Password token token_password: Password token
token_action_create: Create a new token token_action_create: Create a new token
token_action_revoke: Revoke the token token_action_revoke: Revoke the token
token_default_label: Token generated from the web interface token_default_label: Token generated from the web interface
token_action_revoke_confirm: Are you sure you want to revoke this token?