From f4e4febe2c073ad1afe9ca2fcda5d8fe55810d79 Mon Sep 17 00:00:00 2001 From: Eugen Ciur <eugen@papermerge.com> Date: Fri, 11 Feb 2022 22:04:32 +0100 Subject: [PATCH] tokens management --- app/authenticators/auth-token.js | 2 +- app/components/alert/success.hbs | 4 + app/components/commander/index.hbs | 2 +- app/components/tag/new.hbs | 104 +++++++++---------- app/components/token/new.hbs | 47 +++++++++ app/components/token/new.js | 85 +++++++++++++++ app/components/token/table_row.hbs | 14 +++ app/components/token/table_row.js | 13 +++ app/helpers/truncatechars.js | 20 ++++ app/models/token.js | 12 +++ app/router.js | 7 +- app/routes/authenticated/tags.js | 2 +- app/routes/authenticated/tokens.js | 11 ++ app/styles/app.scss | 6 +- app/templates/authenticated/tokens.hbs | 7 ++ app/templates/authenticated/tokens/add.hbs | 1 - app/templates/authenticated/tokens/index.hbs | 1 - 17 files changed, 275 insertions(+), 63 deletions(-) create mode 100644 app/components/alert/success.hbs create mode 100644 app/components/token/new.hbs create mode 100644 app/components/token/new.js create mode 100644 app/components/token/table_row.hbs create mode 100644 app/components/token/table_row.js create mode 100644 app/helpers/truncatechars.js create mode 100644 app/models/token.js create mode 100644 app/routes/authenticated/tokens.js create mode 100644 app/templates/authenticated/tokens.hbs delete mode 100644 app/templates/authenticated/tokens/add.hbs delete mode 100644 app/templates/authenticated/tokens/index.hbs diff --git a/app/authenticators/auth-token.js b/app/authenticators/auth-token.js index 4accc36..8a00242 100644 --- a/app/authenticators/auth-token.js +++ b/app/authenticators/auth-token.js @@ -27,7 +27,7 @@ export default class AuthToken extends Base { async authenticate(username, password) { let response, error; - response = await fetch(`${base_url()}/auth-token/`, { + response = await fetch(`${base_url()}/auth/login/`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/app/components/alert/success.hbs b/app/components/alert/success.hbs new file mode 100644 index 0000000..ed92e0d --- /dev/null +++ b/app/components/alert/success.hbs @@ -0,0 +1,4 @@ +<div class="alert alert-success alert-dismissible fade show" role="alert"> + {{@message}} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> +</div> \ No newline at end of file diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs index 45ed1cb..fc80a2a 100644 --- a/app/components/commander/index.hbs +++ b/app/components/commander/index.hbs @@ -1,4 +1,4 @@ -<div class="panel commander col m-2 p-2" +<div class="panel commander col m-2 p-2 user-select-none" {{droppable onDrop=this.onDrop onDragOver=this.onDragOver }} diff --git a/app/components/tag/new.hbs b/app/components/tag/new.hbs index e1fd42f..51aacff 100644 --- a/app/components/tag/new.hbs +++ b/app/components/tag/new.hbs @@ -12,65 +12,65 @@ </div> </div> - <div class="row mb-2"> - <div class="col-md-3"> - <label for="name" class="form-label">Name</label> - <Input - id="name" - @type="text" - @value={{this.new_name}} - class="form-control" - /> - <div class="d-flex flex-row my-1"> - <Input id="fg_color" @type="color" @value={{this.new_fg_color}} class="form-control form-control-color" /> - <Input id="bg_color" @type="color" @value={{this.new_bg_color}} class="form-control form-control-color" /> - </div> - </div> - <div class="col-md-1"> - <label class="form-check-label" for="is_pinned">Is Pinned?</label> - <div> - <Input - @type="checkbox" - @checked={{this.new_pinned}} - class="form-check-input" id="is_pinned" /> - </div> + <div class="row mb-2"> + <div class="col-md-3"> + <label for="name" class="form-label">Name</label> + <Input + id="name" + @type="text" + @value={{this.new_name}} + class="form-control" + /> + <div class="d-flex flex-row my-1"> + <Input id="fg_color" @type="color" @value={{this.new_fg_color}} class="form-control form-control-color" /> + <Input id="bg_color" @type="color" @value={{this.new_bg_color}} class="form-control form-control-color" /> </div> - <div class="col-md-5"> - <label for="description" class="form-label">Description</label> + </div> + <div class="col-md-1"> + <label class="form-check-label" for="is_pinned">Is Pinned?</label> + <div> <Input - id="description" - @type="text" - @value={{this.new_description}} - class="form-control" - /> + @type="checkbox" + @checked={{this.new_pinned}} + class="form-check-input" id="is_pinned" /> </div> - <div class="col-md-3"> - <label for="action" class="form-label">Action</label> - <div> + </div> + <div class="col-md-5"> + <label for="description" class="form-label">Description</label> + <Input + id="description" + @type="text" + @value={{this.new_description}} + class="form-control" + /> + </div> + <div class="col-md-3"> + <label for="action" class="form-label">Action</label> + <div> + <button + type="button" + {{on "click" this.onCancel}} + class="btn btn-secondary"> + Cancel + </button> + {{#if this.new_name }} <button type="button" - {{on "click" this.onCancel}} - class="btn btn-secondary"> - Cancel + {{on "click" this.onCreate}} + class="btn btn-success"> + Create Tag </button> - {{#if this.new_name }} - <button - type="button" - {{on "click" this.onCreate}} - class="btn btn-success"> - Create Tag - </button> - {{else}} - <button - type="button" - {{on "click" this.onCreate}} - disabled - class="btn btn-success"> - Create Tag - </button> - {{/if}} - </div> + {{else}} + <button + type="button" + {{on "click" this.onCreate}} + disabled + class="btn btn-success"> + Create Tag + </button> + {{/if}} </div> </div> + </div> </form> {{/if}} \ No newline at end of file diff --git a/app/components/token/new.hbs b/app/components/token/new.hbs new file mode 100644 index 0000000..7cda1e5 --- /dev/null +++ b/app/components/token/new.hbs @@ -0,0 +1,47 @@ +{{#if this.message_with_token}} + <Alert::Success @message={{this.message_with_token}} /> +{{/if}} + +<Button::New @onClick={{this.onToggleNew}} class="add-token" /> + +{{#if this.form_visible}} + <form> + <div class="row mb-2"> + <div class="col-md-3"> + <label for="name" class="form-label">Expiry</label> + <div class="input-group mb-3"> + <input type="number" + min="1" + max="365" + class="form-control" + value="{{this.new_expiry}}" + {{on 'change' this.onNumericChange}} > + <select class="form-select" {{on 'change' this.onScaleChange}} > + <option selected value="1">hour(s)</option> + <option value="24">day(s)</option> + <option value="744">month(s)</option> + </select> + </div> + </div> + + <div class="col-md-3"> + <label for="action" class="form-label">Action</label> + <div> + <button + type="button" + {{on "click" this.onCancel}} + class="btn btn-secondary"> + Cancel + </button> + + <button + type="button" + {{on "click" this.onCreate}} + class="btn btn-success"> + Create Token + </button> + </div> + </div> + </div> + </form> +{{/if}} \ No newline at end of file diff --git a/app/components/token/new.js b/app/components/token/new.js new file mode 100644 index 0000000..1ec419b --- /dev/null +++ b/app/components/token/new.js @@ -0,0 +1,85 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { service } from '@ember/service'; + + +export default class NewTokenComponent extends Component { + /* + Component to create new token. + + It consists from a button labeled 'new' which + when clicked will toggle a 'new token' form. + */ + + new_expiry_numeric = 1; + new_expiry_scale = 1; + @service store; + + // initially only 'new' button is visible + @tracked form_visible = false; + @tracked message_with_token; + + @action + onToggleNew() { + this.form_visible = !this.form_visible; + } + + get new_expiry() { + /* + Returns token expiry value, in hours. + */ + let numeric, scale; + + // the numeric value e.g. 3 + numeric = parseInt(this.new_expiry_numeric); + // when user chooses hours, scale := 1 + // when user chooses days, scale := 24 + // when user chooses months, scale := 744 + scale = parseInt(this.new_expiry_scale); + + return numeric * scale; + } + + @action + onCreate() { + let that = this; + + this.store.createRecord('token', { + expiry_hours: this.new_expiry + }).save().then(model => { + let msg; + + msg = "Please remember the token as it won't be displayed again: "; + msg += model.token; + + that.message_with_token = msg; + }); + + this._empty_form(); + } + + @action + onCancel() { + this._empty_form(); + } + + @action + onNumericChange(event) { + this.new_expiry_numeric = parseInt(event.target.value); + } + + @action + onScaleChange(event) { + this.new_expiry_scale = parseInt(event.target.value); + } + + _empty_form() { + /* + Resets the form to initial state + */ + this.new_expiry_scale = 1; + this.new_expiry_numeric = 1; + this.form_visible = false; + } +} diff --git a/app/components/token/table_row.hbs b/app/components/token/table_row.hbs new file mode 100644 index 0000000..1506613 --- /dev/null +++ b/app/components/token/table_row.hbs @@ -0,0 +1,14 @@ +<tr> + <td> + {{truncatechars @token.digest}} + </td> + <td> + {{@token.expiry}} + </td> + <td>{{@token.created}}</td> + <td> + <button class="btn btn-link" type="button" {{on "click" (fn this.onRemove @token)}}> + Remove + </button> + </td> +</tr> \ No newline at end of file diff --git a/app/components/token/table_row.js b/app/components/token/table_row.js new file mode 100644 index 0000000..0fee1aa --- /dev/null +++ b/app/components/token/table_row.js @@ -0,0 +1,13 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { service } from '@ember/service'; + + +export default class TokenTableRowComponent extends Component { + @service store; + + @action + async onRemove(token) { + await token.destroyRecord(); + } +} diff --git a/app/helpers/truncatechars.js b/app/helpers/truncatechars.js new file mode 100644 index 0000000..be2f4ce --- /dev/null +++ b/app/helpers/truncatechars.js @@ -0,0 +1,20 @@ +import { helper } from '@ember/component/helper'; + + +export function truncatechars([str]) { + /* + Truncates a string if it is longer than the specified number of characters. + Truncated strings will end with a translatable ellipsis character ('…'). + */ + if (!str) { + return ''; + } + + if (str.length <= 10) { + return str; + } + + return `${str.substring(0, 10)}...`; +} + +export default helper(truncatechars); diff --git a/app/models/token.js b/app/models/token.js new file mode 100644 index 0000000..4ddcb30 --- /dev/null +++ b/app/models/token.js @@ -0,0 +1,12 @@ +import Model, { attr } from '@ember-data/model'; + + +class TokenModel extends Model { + @attr expiry_hours; + @attr expiry; + @attr digest; + @attr token; + @attr created; +} + +export default TokenModel; diff --git a/app/router.js b/app/router.js index 35b438d..c9e0b99 100644 --- a/app/router.js +++ b/app/router.js @@ -16,6 +16,8 @@ Router.map(function () { this.route('tags'); + this.route('tokens'); + this.route('automates', function () { this.route('add'); this.route('edit', { path: '/:automate_id/edit' }); @@ -44,10 +46,7 @@ Router.map(function () { this.route('section', { path: '/:section_name' }); }); - this.route('tokens', function() { - this.route('add'); - this.route('index', { path: '/' }); - }); + }); this.route('login'); diff --git a/app/routes/authenticated/tags.js b/app/routes/authenticated/tags.js index 67f9b06..0a6b8d5 100644 --- a/app/routes/authenticated/tags.js +++ b/app/routes/authenticated/tags.js @@ -1,4 +1,4 @@ -import { inject as service } from '@ember/service'; +import { service } from '@ember/service'; import BaseRoute from 'papermerge/base/routing'; diff --git a/app/routes/authenticated/tokens.js b/app/routes/authenticated/tokens.js new file mode 100644 index 0000000..924d608 --- /dev/null +++ b/app/routes/authenticated/tokens.js @@ -0,0 +1,11 @@ +import { service } from '@ember/service'; +import BaseRoute from 'papermerge/base/routing'; + + +export default class TokensRoute extends BaseRoute { + @service store; + + async model() { + return this.store.findAll('token'); + } +} diff --git a/app/styles/app.scss b/app/styles/app.scss index 28733cb..ceac81d 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -14,8 +14,10 @@ body { background-color: #e9ecef; - // make node title text unselectable - user-select: none; +} + +.user-select-none { + user-select: none; } main { diff --git a/app/templates/authenticated/tokens.hbs b/app/templates/authenticated/tokens.hbs new file mode 100644 index 0000000..b22ac27 --- /dev/null +++ b/app/templates/authenticated/tokens.hbs @@ -0,0 +1,7 @@ +<Token::New /> + +<Table @titles={{array 'Digest' 'Expiry' 'Created' 'Action' }}> + {{#each @model as |token|}} + <Token::TableRow @token={{token}} /> + {{/each}} +</Table> diff --git a/app/templates/authenticated/tokens/add.hbs b/app/templates/authenticated/tokens/add.hbs deleted file mode 100644 index a319583..0000000 --- a/app/templates/authenticated/tokens/add.hbs +++ /dev/null @@ -1 +0,0 @@ -Add \ No newline at end of file diff --git a/app/templates/authenticated/tokens/index.hbs b/app/templates/authenticated/tokens/index.hbs deleted file mode 100644 index 9f7cda8..0000000 --- a/app/templates/authenticated/tokens/index.hbs +++ /dev/null @@ -1 +0,0 @@ -Index \ No newline at end of file -- GitLab