refactor(UI api token): add more information to api token list
- network filtering - validity - last used remove creation logic
This commit is contained in:
parent
a23eb80d22
commit
b635c940ae
11 changed files with 59 additions and 131 deletions
|
@ -159,5 +159,13 @@
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM7 11V13H17V11H7Z'%3E%3C/path%3E%3C/svg%3E");
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM7 11V13H17V11H7Z'%3E%3C/path%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-key-line {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12.917 13C12.441 15.8377 9.973 18 7 18C3.68629 18 1 15.3137 1 12C1 8.68629 3.68629 6 7 6C9.973 6 12.441 8.16229 12.917 11H23V13H21V17H19V13H17V17H15V13H12.917ZM7 16C9.20914 16 11 14.2091 11 12C11 9.79086 9.20914 8 7 8C4.79086 8 3 9.79086 3 12C3 14.2091 4.79086 16 7 16Z' fill='currentColor'%3E%3C/path%3E%3C/svg%3E");
|
||||||
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12.917 13C12.441 15.8377 9.973 18 7 18C3.68629 18 1 15.3137 1 12C1 8.68629 3.68629 6 7 6C9.973 6 12.441 8.16229 12.917 11H23V13H21V17H19V13H17V17H15V13H12.917ZM7 16C9.20914 16 11 14.2091 11 12C11 9.79086 9.20914 8 7 8C4.79086 8 3 9.79086 3 12C3 14.2091 4.79086 16 7 16Z' fill='currentColor'%3E%3C/path%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
}
|
||||||
// scss-lint:enable VendorPrefix
|
// scss-lint:enable VendorPrefix
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
class Profile::APITokenCardComponent < ApplicationComponent
|
class Profile::APITokenCardComponent < ApplicationComponent
|
||||||
def initialize(created_api_token: nil, created_packed_token: nil)
|
|
||||||
@created_api_token = created_api_token
|
|
||||||
@created_packed_token = created_packed_token
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render?
|
def render?
|
||||||
current_administrateur.present?
|
current_administrateur.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def api_and_packed_tokens
|
def api_tokens
|
||||||
current_administrateur.api_tokens.order(:created_at).map do |api_token|
|
current_administrateur.api_tokens.order(created_at: :desc)
|
||||||
if api_token == @created_api_token && @created_packed_token.present?
|
|
||||||
[api_token, @created_packed_token]
|
|
||||||
else
|
|
||||||
[api_token, nil]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,5 +2,3 @@ en:
|
||||||
tokens_title: API identification tokens
|
tokens_title: API identification tokens
|
||||||
first_paragraph_html: |
|
first_paragraph_html: |
|
||||||
These tokens are needed to make calls to the API of %{application_name}. You can <strong>click on </strong><a target=_blank rel="noopener" href="%{api_doc_url}" title="See %{application_name} API documentation">this link</a> to check our documentation
|
These tokens are needed to make calls to the API of %{application_name}. You can <strong>click on </strong><a target=_blank rel="noopener" href="%{api_doc_url}" title="See %{application_name} API documentation">this link</a> to check our documentation
|
||||||
second_paragraph: If you already have applications that use a token and you revoke it, access to the API will be blocked for those applications.
|
|
||||||
action: Create and display a new token
|
|
||||||
|
|
|
@ -2,5 +2,3 @@ fr:
|
||||||
tokens_title: Jetons d’identification de l’API (token)
|
tokens_title: Jetons d’identification de l’API (token)
|
||||||
first_paragraph_html: |
|
first_paragraph_html: |
|
||||||
Ces jetons sont nécessaires pour effectuer des appels vers l’API de %{application_name}. Vous pouvez <strong>consulter notre documentation en suivant </strong><a target="_blank" rel="noopener" href="%{api_doc_url}" title="Voir la documentation de l'API de %{application_name}">ce lien</a>
|
Ces jetons sont nécessaires pour effectuer des appels vers l’API de %{application_name}. Vous pouvez <strong>consulter notre documentation en suivant </strong><a target="_blank" rel="noopener" href="%{api_doc_url}" title="Voir la documentation de l'API de %{application_name}">ce lien</a>
|
||||||
second_paragraph: Si vous avez déjà des applications qui utilisent un jeton et vous le révoquez, l’accès à l’API sera bloqué pour ces applications.
|
|
||||||
action: Créer et afficher un nouveau jeton
|
|
||||||
|
|
|
@ -3,15 +3,6 @@
|
||||||
= t('.tokens_title')
|
= t('.tokens_title')
|
||||||
%p
|
%p
|
||||||
= t('.first_paragraph_html', application_name: APPLICATION_NAME, api_doc_url: API_DOC_URL)
|
= t('.first_paragraph_html', application_name: APPLICATION_NAME, api_doc_url: API_DOC_URL)
|
||||||
%p
|
|
||||||
= t('.second_paragraph')
|
|
||||||
|
|
||||||
= render Dsfr::ListComponent.new do |list|
|
%ul.fr-mt-4w
|
||||||
- api_and_packed_tokens.each do |(api_token, packed_token)|
|
= render Profile::APITokenComponent.with_collection(api_tokens)
|
||||||
- list.with_item do
|
|
||||||
.fr-card.fr-card--horizontal
|
|
||||||
.fr-card__body.width-100
|
|
||||||
.fr-card__content
|
|
||||||
= render Profile::APITokenComponent.new(api_token:, packed_token:)
|
|
||||||
|
|
||||||
= button_to t('.action'), api_tokens_path, method: :post, class: "fr-btn fr-btn--secondary"
|
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
class Profile::APITokenComponent < ApplicationComponent
|
class Profile::APITokenComponent < ApplicationComponent
|
||||||
def initialize(api_token:, packed_token: nil)
|
def initialize(api_token:)
|
||||||
@api_token = api_token
|
@api_token = api_token
|
||||||
@packed_token = packed_token
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def procedures_to_allow_options
|
def recently_used?
|
||||||
@api_token.targetable_procedures.map { ["#{_1.id} – #{_1.libelle}", _1.id] }
|
@api_token.last_used_at&.> 2.weeks.ago
|
||||||
end
|
end
|
||||||
|
|
||||||
def procedures_to_allow_select_options
|
def autorizations
|
||||||
{ selected: @api_token.targetable_procedures.first&.id }
|
right = @api_token.write_access? ? 'lecture et écriture sur' : 'lecture seule sur'
|
||||||
|
scope = @api_token.full_access? ? 'toutes les démarches' : @api_token.procedures.map(&:libelle).join(', ')
|
||||||
|
sanitize("#{right} #{tag.b(scope)}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def network_filtering
|
||||||
|
if @api_token.authorized_networks.present?
|
||||||
|
"filtrage : #{@api_token.authorized_networks_for_ui}"
|
||||||
|
else
|
||||||
|
tag.span('aucun filtrage réseau', class: 'fr-text-default--warning')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def use_and_expiration
|
||||||
|
use = @api_token.last_used_at.present? ? "utilisé il y a #{time_ago_in_words(@api_token.last_used_at)} - " : ""
|
||||||
|
expiration = @api_token.expires_at.present? ? "valable jusquʼau #{l(@api_token.expires_at, format: :long)}" : "valable indéfiniment"
|
||||||
|
|
||||||
|
"#{use} #{expiration}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
en:
|
|
||||||
allowed_full_access_html: This token has access to <strong>all</strong> the procedures attached to your administrator account
|
|
||||||
allowed_procedures_html:
|
|
||||||
zero: This token has no access to <strong>any</strong> process.
|
|
||||||
one: This token has access to a selected process
|
|
||||||
other: This token has access to %{count} selected procedures
|
|
||||||
security_one: For security reasons, it will not be re-posted, please note.
|
|
||||||
security_two: For security reasons, we can only show it to you when it is created.
|
|
||||||
prompt_choose_procedure: "-- Please, choose a procedure --"
|
|
||||||
security_title: "Security options"
|
|
||||||
action_all: Allow access to all procedures
|
|
||||||
action_choice: "If you want to grant access to selected procedures only :"
|
|
||||||
add: Add
|
|
||||||
delete: Delete
|
|
||||||
token_procedures: This token has access to the procedures
|
|
||||||
revoke_token: Revoke token
|
|
||||||
reading_writing: Reading and writing
|
|
||||||
reading: Read only
|
|
|
@ -1,18 +0,0 @@
|
||||||
fr:
|
|
||||||
allowed_full_access_html: Ce jeton a accès à <strong>toutes</strong> les démarches attachées à votre compte administrateur
|
|
||||||
allowed_procedures_html:
|
|
||||||
zero: Ce jeton n’a accès à <strong>aucune</strong> démarche
|
|
||||||
one: Ce jeton a accès à une démarche sélectionnée
|
|
||||||
other: Ce jeton a accès à %{count} démarches sélectionnées
|
|
||||||
security_one: Pour des raisons de sécurité, il ne sera plus ré-affiché, notez-le bien.
|
|
||||||
security_two: Pour des raisons de sécurité, nous ne pouvons vous l’afficher que lors de sa création.
|
|
||||||
security_title: "Options de sécurité"
|
|
||||||
action_all: Autoriser l’accès a toutes les démarches
|
|
||||||
prompt_choose_procedure: "-- Veuillez sélectionner une procédure à ajouter --"
|
|
||||||
action_choice: Si vous souhaitez autoriser l’accès seulement a des démarches choisies, ajouter les au jeton
|
|
||||||
add: Ajouter
|
|
||||||
delete: Supprimer
|
|
||||||
token_procedures: Ce jeton a accès aux démarches
|
|
||||||
revoke_token: Révoquer le jeton
|
|
||||||
reading_writing: En lecture et écriture
|
|
||||||
reading: En lecture seule
|
|
|
@ -1,60 +1,16 @@
|
||||||
%h3.fr-card__title
|
%li.fr-mt-2w.flex
|
||||||
%b= "#{@api_token.name} "
|
.fr-mr-4w{ class: class_names('fr-text-default--success': recently_used?) }
|
||||||
%span.fr-text--sm= @api_token.prefix
|
%span.fr-icon-key-line
|
||||||
|
.flex-grow
|
||||||
.fr-card__desc
|
%div
|
||||||
- if @packed_token.present?
|
%b= @api_token.name
|
||||||
.fr-text--sm{ style: "width: 80%; word-break: break-all;" }
|
%span (commence par #{@api_token.prefix})
|
||||||
- button = render Dsfr::CopyButtonComponent.new(text: @packed_token, title: "Copier le jeton dans le presse-papier", success: "Le jeton a été copié dans le presse-papier")
|
%div= autorizations
|
||||||
= "#{@packed_token} #{button}"
|
%div= network_filtering
|
||||||
|
%div= use_and_expiration
|
||||||
%p
|
%div
|
||||||
= t('.security_one')
|
= link_to 'Supprimer',
|
||||||
|
api_token_path(@api_token),
|
||||||
- else
|
method: :delete,
|
||||||
%p
|
class: 'fr-btn fr-btn--tertiary-no-outline fr-btn--sm fr-btn--icon-left fr-icon-delete-line',
|
||||||
= t('.security_two')
|
data: { confirm: "Confirmez-vous la suppression du jeton « #{@api_token.name} » ?" }
|
||||||
|
|
||||||
= render Dsfr::AlertComponent.new(state: :info, title: t(".security_title"), heading_level: :h4) do |c|
|
|
||||||
- c.with_body do
|
|
||||||
- if @api_token.full_access?
|
|
||||||
%p.fr-text--lg
|
|
||||||
= t('.allowed_full_access_html')
|
|
||||||
- else
|
|
||||||
%p.fr-text--lg
|
|
||||||
= t('.allowed_procedures_html', count: @api_token.procedures.size)
|
|
||||||
|
|
||||||
- if @api_token.procedures.empty?
|
|
||||||
= button_to t('.action_all'), @api_token, method: :patch, params: { api_token: { become_full_access: '1' } }, class: "fr-btn fr-btn--secondary"
|
|
||||||
- else
|
|
||||||
%ul
|
|
||||||
- @api_token.procedures.each do |procedure|
|
|
||||||
%li.flex.justify-between.align-center
|
|
||||||
.truncate-80
|
|
||||||
= "#{procedure.id} – #{procedure.libelle}"
|
|
||||||
= button_to t('.delete'), @api_token, method: :patch, params: { api_token: { disallow_procedure_id: procedure.id } }, class: "fr-btn fr-btn--secondary"
|
|
||||||
|
|
||||||
.fr-card__end
|
|
||||||
= form_for @api_token, namespace: dom_id(@api_token, :allowed_procedures), html: { class: 'mb-3', data: { turbo: true } } do |f|
|
|
||||||
= f.label :allowed_procedure_ids, class: 'fr-label' do
|
|
||||||
= t('.action_choice')
|
|
||||||
- if !@api_token.full_access?
|
|
||||||
- @api_token.procedures.each do |procedure|
|
|
||||||
= f.hidden_field :allowed_procedure_ids, value: procedure.id, multiple: true, id: dom_id(procedure, :allowed_procedure)
|
|
||||||
.flex.justify-between.align-center{ 'data-turbo-force': :server }
|
|
||||||
= f.select :allowed_procedure_ids, procedures_to_allow_options, {prompt: t('.prompt_choose_procedure')}, { class: 'fr-select ', name: "api_token[allowed_procedure_ids][]" }
|
|
||||||
= f.button type: :submit, class: "fr-btn fr-btn--secondary" do
|
|
||||||
= t('.add')
|
|
||||||
|
|
||||||
= form_for @api_token, namespace: dom_id(@api_token, :write_access), html: { class: 'form mb-3', data: { turbo: true, controller: 'autosubmit' } } do |f|
|
|
||||||
= f.label :write_access do
|
|
||||||
= t('.token_procedures')
|
|
||||||
%label.toggle-switch.no-margin
|
|
||||||
= f.check_box :write_access, class: 'toggle-switch-checkbox'
|
|
||||||
%span.toggle-switch-control.round
|
|
||||||
%span.toggle-switch-label.on
|
|
||||||
= t('.reading_writing')
|
|
||||||
%span.toggle-switch-label.off
|
|
||||||
= t('.reading')
|
|
||||||
|
|
||||||
= button_to t('.revoke_token'), api_token_path(@api_token), method: :delete, class: "fr-btn fr-btn--secondary", data: { turbo_confirm: "Confirmez-vous la révocation de ce jeton ? Les applications qui l’utilisent actuellement seront bloquées." }
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class APITokensController < ApplicationController
|
||||||
def destroy
|
def destroy
|
||||||
@api_token.destroy
|
@api_token.destroy
|
||||||
|
|
||||||
render :index
|
redirect_to profil_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -65,6 +65,10 @@ class APIToken < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorized_networks_for_ui
|
||||||
|
authorized_networks.map { "#{_1.to_string}/#{_1.prefix}" }.join(', ')
|
||||||
|
end
|
||||||
|
|
||||||
def forbidden_network?(ip)
|
def forbidden_network?(ip)
|
||||||
return false if authorized_networks.blank?
|
return false if authorized_networks.blank?
|
||||||
|
|
||||||
|
@ -97,6 +101,10 @@ class APIToken < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def last_used_at
|
||||||
|
last_v2_authenticated_at || last_v1_authenticated_at
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def sanitize_targeted_procedure_ids
|
def sanitize_targeted_procedure_ids
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue