Merge pull request #9391 from demarches-simplifiees/remove_api_token_v1_v2_logic
Tech: suppression du code d'authentification des jetons v1 et v2
This commit is contained in:
commit
39368ab674
14 changed files with 362 additions and 493 deletions
|
@ -7,10 +7,10 @@ class Profile::APITokenComponent < ApplicationComponent
|
||||||
private
|
private
|
||||||
|
|
||||||
def procedures_to_allow_options
|
def procedures_to_allow_options
|
||||||
@api_token.procedures_to_allow.map { ["#{_1.id} – #{_1.libelle}", _1.id] }
|
@api_token.targetable_procedures.map { ["#{_1.id} – #{_1.libelle}", _1.id] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def procedures_to_allow_select_options
|
def procedures_to_allow_select_options
|
||||||
{ selected: @api_token.procedures_to_allow.first&.id }
|
{ selected: @api_token.targetable_procedures.first&.id }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,26 +27,27 @@
|
||||||
= t('.allowed_full_access_html')
|
= t('.allowed_full_access_html')
|
||||||
- else
|
- else
|
||||||
%p.fr-text--lg
|
%p.fr-text--lg
|
||||||
= t('.allowed_procedures_html', count: @api_token.allowed_procedures.size)
|
= t('.allowed_procedures_html', count: @api_token.procedures.size)
|
||||||
|
|
||||||
- if @api_token.allowed_procedures.empty?
|
- if @api_token.procedures.empty?
|
||||||
= button_to t('.action_all'), @api_token, method: :patch, params: { api_token: { disallow_procedure_id: '0' } }, class: "fr-btn fr-btn--secondary"
|
= button_to t('.action_all'), @api_token, method: :patch, params: { api_token: { become_full_access: '1' } }, class: "fr-btn fr-btn--secondary"
|
||||||
- else
|
- else
|
||||||
%ul
|
%ul
|
||||||
- @api_token.allowed_procedures.each do |procedure|
|
- @api_token.procedures.each do |procedure|
|
||||||
%li.flex.justify-between.align-center
|
%li.flex.justify-between.align-center
|
||||||
.truncate-80
|
.truncate-80
|
||||||
= "#{procedure.id} – #{procedure.libelle}"
|
= "#{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"
|
= 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
|
.fr-card__end
|
||||||
= form_for @api_token, namespace: dom_id(@api_token, :allowed_procedures), html: { class: 'form form-ds-fr-white mb-3', data: { turbo: true } } do |f|
|
= 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 do
|
= f.label :allowed_procedure_ids, class: 'fr-label' do
|
||||||
= t('.action_choice')
|
= t('.action_choice')
|
||||||
- @api_token.allowed_procedures.each do |procedure|
|
- if !@api_token.full_access?
|
||||||
= f.hidden_field :allowed_procedure_ids, value: procedure.id, multiple: true, id: dom_id(procedure, :allowed_procedure)
|
- @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 }
|
.flex.justify-between.align-center{ 'data-turbo-force': :server }
|
||||||
= f.select :allowed_procedure_ids, procedures_to_allow_options, {prompt: t('.prompt_choose_procedure')}, { class: 'no-margin width-66 small', name: "api_token[allowed_procedure_ids][]" }
|
= 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
|
= f.button type: :submit, class: "fr-btn fr-btn--secondary" do
|
||||||
= t('.add')
|
= t('.add')
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class API::V1::DossiersController < APIController
|
class API::V1::DossiersController < APIController
|
||||||
before_action :fetch_procedure_and_check_token
|
before_action :check_api_token
|
||||||
|
before_action :fetch_dossiers
|
||||||
|
|
||||||
DEFAULT_PAGE_SIZE = 100
|
DEFAULT_PAGE_SIZE = 100
|
||||||
MAX_PAGE_SIZE = 1000
|
MAX_PAGE_SIZE = 1000
|
||||||
|
@ -8,19 +9,17 @@ class API::V1::DossiersController < APIController
|
||||||
def index
|
def index
|
||||||
dossiers = @dossiers.page(params[:page]).per(per_page)
|
dossiers = @dossiers.page(params[:page]).per(per_page)
|
||||||
|
|
||||||
render json: { dossiers: dossiers.map { |dossier| DossiersSerializer.new(dossier) }, pagination: pagination(dossiers) }, status: 200
|
render json: { dossiers: dossiers.map { |dossier| DossiersSerializer.new(dossier) }, pagination: pagination(dossiers) }
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: {}, status: 404
|
render json: {}, status: :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
dossier = @dossiers.for_api.find(params[:id])
|
dossier = @dossiers.for_api.find(params[:id])
|
||||||
|
|
||||||
respond_to do |format|
|
render json: { dossier: DossierSerializer.new(dossier).as_json }
|
||||||
format.json { render json: { dossier: DossierSerializer.new(dossier).as_json }, status: 200 }
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: {}, status: 404
|
render json: {}, status: :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -42,24 +41,15 @@ class API::V1::DossiersController < APIController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_procedure_and_check_token
|
def fetch_dossiers
|
||||||
@procedure = Procedure.for_api.find(params[:procedure_id])
|
procedure = @api_token.procedures.find(params[:procedure_id])
|
||||||
|
|
||||||
administrateur = find_administrateur_for_token(@procedure)
|
order = ORDER_DIRECTIONS.fetch(params[:order], :asc)
|
||||||
if administrateur.nil?
|
@dossiers = procedure
|
||||||
render json: {}, status: :unauthorized
|
.dossiers
|
||||||
else
|
.visible_by_administration
|
||||||
# allow BaseController append_info_to_payload
|
.order_by_created_at(order)
|
||||||
# to log info on current_user
|
|
||||||
@current_user = administrateur.user
|
|
||||||
|
|
||||||
order = ORDER_DIRECTIONS.fetch(params[:order], :asc)
|
|
||||||
@dossiers = @procedure
|
|
||||||
.dossiers
|
|
||||||
.visible_by_administration
|
|
||||||
.order_by_created_at(order)
|
|
||||||
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: {}, status: :not_found
|
render json: {}, status: :not_found
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class API::V1::ProceduresController < APIController
|
class API::V1::ProceduresController < APIController
|
||||||
before_action :fetch_procedure_and_check_token
|
before_action :check_api_token
|
||||||
|
before_action :fetch_procedure
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render json: { procedure: ProcedureSerializer.new(@procedure).as_json }
|
render json: { procedure: ProcedureSerializer.new(@procedure).as_json }
|
||||||
|
@ -7,17 +8,9 @@ class API::V1::ProceduresController < APIController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fetch_procedure_and_check_token
|
def fetch_procedure
|
||||||
@procedure = Procedure.for_api.find(params[:id])
|
@procedure = @api_token.procedures.for_api.find(params[:id])
|
||||||
|
|
||||||
administrateur = find_administrateur_for_token(@procedure)
|
|
||||||
if administrateur.nil?
|
|
||||||
render json: {}, status: :unauthorized
|
|
||||||
else
|
|
||||||
# allow BaseController append_info_to_payload
|
|
||||||
# to log info on current_user
|
|
||||||
@current_user = administrateur.user
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: {}, status: :not_found
|
render json: {}, status: :not_found
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,58 +1,45 @@
|
||||||
class API::V2::BaseController < ApplicationController
|
class API::V2::BaseController < ApplicationController
|
||||||
# Disable forgery protection for API controllers when the request is authenticated
|
skip_forgery_protection if: -> { request.headers.key?('HTTP_AUTHORIZATION') }
|
||||||
# with a bearer token. Otherwise the session will be nullified and we'll lose curent_user
|
|
||||||
protect_from_forgery with: :null_session, unless: :token?
|
|
||||||
skip_before_action :setup_tracking
|
skip_before_action :setup_tracking
|
||||||
prepend_before_action :authenticate_administrateur_from_token
|
before_action :authenticate_from_token
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def context
|
def context
|
||||||
# new token
|
if @api_token.present?
|
||||||
if api_token.present?
|
@api_token.context
|
||||||
api_token.context
|
|
||||||
# web interface (/graphql) give current_administrateur
|
# web interface (/graphql) give current_administrateur
|
||||||
elsif current_administrateur.present?
|
elsif current_administrateur.present?
|
||||||
{
|
graphql_web_interface_context
|
||||||
administrateur_id: current_administrateur.id,
|
|
||||||
procedure_ids: current_administrateur.procedure_ids,
|
|
||||||
write_access: true
|
|
||||||
}
|
|
||||||
# old token
|
|
||||||
else
|
else
|
||||||
{
|
unauthenticated_request_context
|
||||||
token: authorization_bearer_token,
|
|
||||||
write_access: true
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def token?
|
private
|
||||||
authorization_bearer_token.present?
|
|
||||||
|
def graphql_web_interface_context
|
||||||
|
{
|
||||||
|
administrateur_id: current_administrateur.id,
|
||||||
|
procedure_ids: current_administrateur.procedure_ids,
|
||||||
|
write_access: true
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_administrateur_from_token
|
def unauthenticated_request_context
|
||||||
if api_token.present?
|
{
|
||||||
@current_user = api_token.administrateur.user
|
administrateur_id: nil,
|
||||||
end
|
procedure_ids: [],
|
||||||
|
write_access: false
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def api_token
|
def authenticate_from_token
|
||||||
if @api_token.nil?
|
@api_token = authenticate_with_http_token { |t, _o| APIToken.authenticate(t) }
|
||||||
@api_token = APIToken
|
|
||||||
.find_and_verify(authorization_bearer_token)
|
|
||||||
&.tap { _1.touch(:last_v2_authenticated_at) } || false
|
|
||||||
end
|
|
||||||
@api_token
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorization_bearer_token
|
if @api_token.present?
|
||||||
@authorization_bearer_token ||= begin
|
@api_token.touch(:last_v2_authenticated_at)
|
||||||
received_token = nil
|
@current_user = @api_token.administrateur.user
|
||||||
authenticate_with_http_token do |token, _options|
|
|
||||||
received_token = token
|
|
||||||
end
|
|
||||||
received_token
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
class APIController < ApplicationController
|
class APIController < ApplicationController
|
||||||
before_action :default_format_json
|
before_action :default_format_json
|
||||||
|
before_action :authenticate_from_token
|
||||||
protected
|
|
||||||
|
|
||||||
def find_administrateur_for_token(procedure)
|
|
||||||
api_token = APIToken.find_and_verify(authorization_bearer_token, procedure.administrateurs)
|
|
||||||
if api_token.present? && api_token.context.fetch(:procedure_ids).include?(procedure.id)
|
|
||||||
api_token.touch(:last_v1_authenticated_at)
|
|
||||||
api_token.administrateur
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
@ -17,19 +8,24 @@ class APIController < ApplicationController
|
||||||
request.format = "json" if !request.params[:format]
|
request.format = "json" if !request.params[:format]
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorization_bearer_token
|
def check_api_token
|
||||||
params_token.presence || header_token
|
if @api_token.nil?
|
||||||
end
|
render json: {}, status: :unauthorized
|
||||||
|
|
||||||
def header_token
|
|
||||||
received_token = nil
|
|
||||||
authenticate_with_http_token do |token, _options|
|
|
||||||
received_token = token
|
|
||||||
end
|
end
|
||||||
received_token
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def params_token
|
def authenticate_from_token
|
||||||
params[:token]
|
@api_token = authenticate_with_http_token { |t, _o| APIToken.authenticate(t) }
|
||||||
|
|
||||||
|
# legacy way of sending the token by url
|
||||||
|
# not available in api v2
|
||||||
|
if @api_token.nil?
|
||||||
|
@api_token = APIToken.authenticate(params[:token])
|
||||||
|
end
|
||||||
|
|
||||||
|
if @api_token.present?
|
||||||
|
@api_token.touch(:last_v1_authenticated_at)
|
||||||
|
@current_user = @api_token.administrateur.user
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,44 +1,46 @@
|
||||||
class APITokensController < ApplicationController
|
class APITokensController < ApplicationController
|
||||||
before_action :authenticate_administrateur!
|
before_action :authenticate_administrateur!
|
||||||
|
before_action :set_api_token, only: [:update, :destroy]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@api_token, @packed_token = APIToken.generate(current_administrateur)
|
@api_token, @packed_token = APIToken.generate(current_administrateur)
|
||||||
|
|
||||||
respond_to do |format|
|
render :index
|
||||||
format.turbo_stream { render :index }
|
|
||||||
format.html { redirect_back(fallback_location: profil_path) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@api_token = current_administrateur.api_tokens.find(params[:id])
|
if become_full_access?
|
||||||
|
@api_token.become_full_access!
|
||||||
disallow_procedure_id = api_token_params.fetch(:disallow_procedure_id, nil)
|
elsif disallow_procedure_id.present?
|
||||||
if disallow_procedure_id.present?
|
@api_token.untarget_procedure(disallow_procedure_id.to_i)
|
||||||
@api_token.disallow_procedure(disallow_procedure_id.to_i)
|
|
||||||
else
|
else
|
||||||
@api_token.update!(api_token_params)
|
@api_token.update!(api_token_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
render :index
|
||||||
format.turbo_stream { render :index }
|
|
||||||
format.html { redirect_back(fallback_location: profil_path) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@api_token = current_administrateur.api_tokens.find(params[:id])
|
|
||||||
@api_token.destroy
|
@api_token.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
render :index
|
||||||
format.turbo_stream { render :index }
|
|
||||||
format.html { redirect_back(fallback_location: profil_path) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_api_token
|
||||||
|
@api_token = current_administrateur.api_tokens.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def become_full_access?
|
||||||
|
api_token_params[:become_full_access].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def disallow_procedure_id
|
||||||
|
api_token_params[:disallow_procedure_id]
|
||||||
|
end
|
||||||
|
|
||||||
def api_token_params
|
def api_token_params
|
||||||
params.require(:api_token).permit(:name, :write_access, :disallow_procedure_id, allowed_procedure_ids: [])
|
params.require(:api_token).permit(:name, :write_access, :become_full_access, :disallow_procedure_id, allowed_procedure_ids: [])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,20 +75,7 @@ class API::V2::Context < GraphQL::Query::Context
|
||||||
|
|
||||||
def compute_demarche_authorization(demarche)
|
def compute_demarche_authorization(demarche)
|
||||||
# procedure_ids and token are passed from graphql controller
|
# procedure_ids and token are passed from graphql controller
|
||||||
if self[:procedure_ids].present?
|
self[:procedure_ids].include?(demarche.id)
|
||||||
self[:procedure_ids].include?(demarche.id)
|
|
||||||
elsif self[:token].present?
|
|
||||||
token = APIToken.find_and_verify(self[:token], demarche.administrateurs)
|
|
||||||
if token.present?
|
|
||||||
token.touch(:last_v2_authenticated_at)
|
|
||||||
Current.user = token.administrateur.user
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is a query AST visitor that we use to check
|
# This is a query AST visitor that we use to check
|
||||||
|
|
|
@ -2,42 +2,53 @@ class APIToken < ApplicationRecord
|
||||||
include ActiveRecord::SecureToken
|
include ActiveRecord::SecureToken
|
||||||
|
|
||||||
belongs_to :administrateur, inverse_of: :api_tokens
|
belongs_to :administrateur, inverse_of: :api_tokens
|
||||||
has_many :procedures, through: :administrateur
|
|
||||||
|
|
||||||
before_save :check_allowed_procedure_ids_ownership
|
before_save :sanitize_targeted_procedure_ids
|
||||||
|
|
||||||
def context
|
def context
|
||||||
context = { administrateur_id: administrateur_id, write_access: write_access? }
|
{
|
||||||
|
administrateur_id:,
|
||||||
|
procedure_ids:,
|
||||||
|
write_access:
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def procedure_ids
|
||||||
if full_access?
|
if full_access?
|
||||||
context.merge procedure_ids:
|
administrateur.procedures.ids
|
||||||
else
|
else
|
||||||
context.merge procedure_ids: procedure_ids & allowed_procedure_ids
|
sanitized_targeted_procedure_ids
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def procedures
|
||||||
|
Procedure.where(id: procedure_ids)
|
||||||
|
end
|
||||||
|
|
||||||
def full_access?
|
def full_access?
|
||||||
allowed_procedure_ids.nil?
|
targeted_procedure_ids.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def procedures_to_allow
|
def targetable_procedures
|
||||||
procedures.select(:id, :libelle, :path).where.not(id: allowed_procedure_ids || []).order(:libelle)
|
administrateur
|
||||||
|
.procedures
|
||||||
|
.where.not(id: targeted_procedure_ids)
|
||||||
|
.select(:id, :libelle, :path)
|
||||||
|
.order(:libelle)
|
||||||
end
|
end
|
||||||
|
|
||||||
def allowed_procedures
|
def untarget_procedure(procedure_id)
|
||||||
if allowed_procedure_ids.present?
|
new_target_ids = targeted_procedure_ids - [procedure_id]
|
||||||
procedures.select(:id, :libelle, :path).where(id: allowed_procedure_ids).order(:libelle)
|
|
||||||
else
|
update!(allowed_procedure_ids: new_target_ids)
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def disallow_procedure(procedure_id)
|
def sanitized_targeted_procedure_ids
|
||||||
allowed_procedure_ids = allowed_procedures.map(&:id) - [procedure_id]
|
administrateur.procedures.ids.intersection(targeted_procedure_ids || [])
|
||||||
if allowed_procedure_ids.empty?
|
end
|
||||||
allowed_procedure_ids = nil
|
|
||||||
end
|
def become_full_access!
|
||||||
update!(allowed_procedure_ids:)
|
update_column(:allowed_procedure_ids, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Prefix is made of the first 6 characters of the uuid base64 encoded
|
# Prefix is made of the first 6 characters of the uuid base64 encoded
|
||||||
|
@ -51,67 +62,46 @@ class APIToken < ApplicationRecord
|
||||||
plain_token = generate_unique_secure_token
|
plain_token = generate_unique_secure_token
|
||||||
encrypted_token = BCrypt::Password.create(plain_token)
|
encrypted_token = BCrypt::Password.create(plain_token)
|
||||||
api_token = create!(administrateur:, encrypted_token:, name: Date.today.strftime('Jeton d’API généré le %d/%m/%Y'))
|
api_token = create!(administrateur:, encrypted_token:, name: Date.today.strftime('Jeton d’API généré le %d/%m/%Y'))
|
||||||
packed_token = Base64.urlsafe_encode64([api_token.id, plain_token].join(';'))
|
bearer = BearerToken.new(api_token.id, plain_token)
|
||||||
[api_token, packed_token]
|
[api_token, bearer.to_string]
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_and_verify(maybe_packed_token, administrateurs = [])
|
def authenticate(bearer_string)
|
||||||
token = case unpack(maybe_packed_token)
|
bearer = BearerToken.from_string(bearer_string)
|
||||||
in { plain_token:, id: } # token v3
|
|
||||||
find_by(id:, version: 3)&.then(&ensure_valid_token(plain_token))
|
|
||||||
in { plain_token:, administrateur_id: } # token v2
|
|
||||||
# the migration to the APIToken model set `version: 1` for all the v1 and v2 token
|
|
||||||
# this is the only place where we can fix the version
|
|
||||||
where(administrateur_id:, version: 1).update_all(version: 2) # update to v2
|
|
||||||
find_by(administrateur_id:, version: 2)&.then(&ensure_valid_token(plain_token))
|
|
||||||
in { plain_token: } # token v1
|
|
||||||
where(administrateur: administrateurs, version: 1).find(&ensure_valid_token(plain_token))
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO:
|
return if bearer.nil?
|
||||||
# remove all the not v3 version code
|
|
||||||
# when everyone has migrated
|
|
||||||
# it should also be a good place in case we need to feature flag old token use
|
|
||||||
if token&.version == 3 || Rails.env.test?
|
|
||||||
token
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
api_token = find_by(id: bearer.api_token_id, version: 3)
|
||||||
|
|
||||||
UUID_SIZE = SecureRandom.uuid.size
|
return if api_token.nil?
|
||||||
def unpack(maybe_packed_token)
|
|
||||||
case message_verifier.verified(maybe_packed_token)
|
|
||||||
in [administrateur_id, plain_token]
|
|
||||||
{ plain_token:, administrateur_id: }
|
|
||||||
else
|
|
||||||
case Base64.urlsafe_decode64(maybe_packed_token).split(';')
|
|
||||||
in [id, plain_token] if id.size == UUID_SIZE # valid format "<uuid>;<random token>"
|
|
||||||
{ plain_token:, id: }
|
|
||||||
else
|
|
||||||
{ plain_token: maybe_packed_token }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
{ plain_token: maybe_packed_token }
|
|
||||||
end
|
|
||||||
|
|
||||||
def message_verifier
|
BCrypt::Password.new(api_token.encrypted_token) == bearer.plain_token ? api_token : nil
|
||||||
Rails.application.message_verifier('api_v2_token')
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_valid_token(plain_token)
|
|
||||||
-> (api_token) { api_token if BCrypt::Password.new(api_token.encrypted_token) == plain_token }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_allowed_procedure_ids_ownership
|
def sanitize_targeted_procedure_ids
|
||||||
if allowed_procedure_ids.present?
|
if targeted_procedure_ids.present?
|
||||||
self.allowed_procedure_ids = allowed_procedures.map(&:id)
|
write_attribute(:allowed_procedure_ids, sanitized_targeted_procedure_ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def targeted_procedure_ids
|
||||||
|
read_attribute(:allowed_procedure_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
class BearerToken < Data.define(:api_token_id, :plain_token)
|
||||||
|
def to_string
|
||||||
|
Base64.urlsafe_encode64([api_token_id, plain_token].join(';'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_string(bearer_token)
|
||||||
|
return if bearer_token.nil?
|
||||||
|
|
||||||
|
api_token_id, plain_token = Base64.urlsafe_decode64(bearer_token).split(';')
|
||||||
|
BearerToken.new(api_token_id, plain_token)
|
||||||
|
rescue ArgumentError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe API::V1::DossiersController do
|
||||||
|
|
||||||
context 'when procedure does not belong to admin' do
|
context 'when procedure does not belong to admin' do
|
||||||
let(:procedure_id) { wrong_procedure.id }
|
let(:procedure_id) { wrong_procedure.id }
|
||||||
it { expect(subject.code).to eq('401') }
|
it { expect(subject.code).to eq('404') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when procedure is found and belongs to admin' do
|
context 'when procedure is found and belongs to admin' do
|
||||||
|
@ -43,28 +43,30 @@ describe API::V1::DossiersController do
|
||||||
let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, :en_construction, procedure: procedure) } }
|
let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, :en_construction, procedure: procedure) } }
|
||||||
let(:body) { JSON.parse(retour.body, symbolize_names: true) }
|
let(:body) { JSON.parse(retour.body, symbolize_names: true) }
|
||||||
|
|
||||||
it 'return REST code 200', :show_in_doc do
|
it do
|
||||||
expect(retour.code).to eq('200')
|
expect(retour.code).to eq('200')
|
||||||
|
expect(body).to have_key :pagination
|
||||||
|
expect(body).to have_key :dossiers
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(body).to have_key :pagination }
|
context 'but the token is invalid' do
|
||||||
|
let(:token) { 'bad' }
|
||||||
|
|
||||||
it { expect(body).to have_key :dossiers }
|
it { expect(subject.code).to eq('401') }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'pagination' do
|
describe 'pagination' do
|
||||||
subject { body[:pagination] }
|
subject { body[:pagination] }
|
||||||
it { is_expected.to have_key(:page) }
|
it do
|
||||||
it { expect(subject[:page]).to eq(1) }
|
expect(subject[:page]).to eq(1)
|
||||||
it { is_expected.to have_key(:resultats_par_page) }
|
expect(subject[:resultats_par_page]).to eq(described_class.const_get(:DEFAULT_PAGE_SIZE))
|
||||||
it { expect(subject[:resultats_par_page]).to eq(described_class.const_get(:DEFAULT_PAGE_SIZE)) }
|
expect(subject[:nombre_de_page]).to eq(1)
|
||||||
it { is_expected.to have_key(:nombre_de_page) }
|
end
|
||||||
it { expect(subject[:nombre_de_page]).to eq(1) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with custom resultats_par_page' do
|
describe 'with custom resultats_par_page' do
|
||||||
let(:retour) { get :index, params: { token: token, procedure_id: procedure_id, resultats_par_page: 18 } }
|
let(:retour) { get :index, params: { token: token, procedure_id: procedure_id, resultats_par_page: 18 } }
|
||||||
subject { body[:pagination] }
|
subject { body[:pagination] }
|
||||||
it { is_expected.to have_key(:resultats_par_page) }
|
|
||||||
it { expect(subject[:resultats_par_page]).to eq(18) }
|
it { expect(subject[:resultats_par_page]).to eq(18) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -73,11 +75,14 @@ describe API::V1::DossiersController do
|
||||||
it { expect(subject).to be_an(Array) }
|
it { expect(subject).to be_an(Array) }
|
||||||
describe 'dossier' do
|
describe 'dossier' do
|
||||||
subject { super().first }
|
subject { super().first }
|
||||||
it { expect(subject[:id]).to eq(dossier.id) }
|
|
||||||
it { expect(subject[:updated_at]).to eq("2008-09-01T08:05:00.000Z") }
|
it do
|
||||||
it { expect(subject[:initiated_at]).to eq("2008-09-01T08:06:00.000Z") }
|
expect(subject[:id]).to eq(dossier.id)
|
||||||
it { expect(subject[:state]).to eq("initiated") }
|
expect(subject[:updated_at]).to eq("2008-09-01T08:05:00.000Z")
|
||||||
it { expect(subject.keys.size).to eq(4) }
|
expect(subject[:initiated_at]).to eq("2008-09-01T08:06:00.000Z")
|
||||||
|
expect(subject[:state]).to eq("initiated")
|
||||||
|
expect(subject.keys.size).to eq(4)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'order' do
|
describe 'order' do
|
||||||
|
@ -136,7 +141,7 @@ describe API::V1::DossiersController do
|
||||||
context 'when procedure exists and does not belong to current admin' do
|
context 'when procedure exists and does not belong to current admin' do
|
||||||
let(:procedure_id) { wrong_procedure.id }
|
let(:procedure_id) { wrong_procedure.id }
|
||||||
let(:dossier_id) { 1 }
|
let(:dossier_id) { 1 }
|
||||||
it { expect(subject.code).to eq('401') }
|
it { expect(subject.code).to eq('404') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when procedure is found and belongs to current admin' do
|
context 'when procedure is found and belongs to current admin' do
|
||||||
|
@ -181,13 +186,14 @@ describe API::V1::DossiersController do
|
||||||
expect(retour.code).to eq('200')
|
expect(retour.code).to eq('200')
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(subject[:id]).to eq(dossier.id) }
|
it do
|
||||||
it { expect(subject[:state]).to eq('closed') }
|
expect(subject[:id]).to eq(dossier.id)
|
||||||
it { expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z') }
|
expect(subject[:state]).to eq('closed')
|
||||||
it { expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z') }
|
expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z')
|
||||||
it { expect(subject[:archived]).to eq(dossier.archived) }
|
expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z')
|
||||||
|
expect(subject[:archived]).to eq(dossier.archived)
|
||||||
it { expect(subject.keys).to match_array(field_list) }
|
expect(subject.keys).to match_array(field_list)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'entreprise' do
|
describe 'entreprise' do
|
||||||
let(:field_list) {
|
let(:field_list) {
|
||||||
|
@ -213,17 +219,19 @@ describe API::V1::DossiersController do
|
||||||
}
|
}
|
||||||
subject { super()[:entreprise] }
|
subject { super()[:entreprise] }
|
||||||
|
|
||||||
it { expect(subject[:siren]).to eq('440117620') }
|
it do
|
||||||
it { expect(subject[:capital_social]).to eq(537_100_000) }
|
expect(subject[:siren]).to eq('440117620')
|
||||||
it { expect(subject[:numero_tva_intracommunautaire]).to eq('FR27440117620') }
|
expect(subject[:capital_social]).to eq(537_100_000)
|
||||||
it { expect(subject[:forme_juridique]).to eq('SA à conseil d\'administration (s.a.i.)') }
|
expect(subject[:numero_tva_intracommunautaire]).to eq('FR27440117620')
|
||||||
it { expect(subject[:forme_juridique_code]).to eq('5599') }
|
expect(subject[:forme_juridique]).to eq('SA à conseil d\'administration (s.a.i.)')
|
||||||
it { expect(subject[:nom_commercial]).to eq('GRTGAZ') }
|
expect(subject[:forme_juridique_code]).to eq('5599')
|
||||||
it { expect(subject[:raison_sociale]).to eq('GRTGAZ') }
|
expect(subject[:nom_commercial]).to eq('GRTGAZ')
|
||||||
it { expect(subject[:siret_siege_social]).to eq('44011762001530') }
|
expect(subject[:raison_sociale]).to eq('GRTGAZ')
|
||||||
it { expect(subject[:code_effectif_entreprise]).to eq('51') }
|
expect(subject[:siret_siege_social]).to eq('44011762001530')
|
||||||
it { expect(subject[:date_creation]).to eq('1990-04-24T00:00:00.000+00:00') }
|
expect(subject[:code_effectif_entreprise]).to eq('51')
|
||||||
it { expect(subject.keys).to match_array(field_list) }
|
expect(subject[:date_creation]).to eq('1990-04-24T00:00:00.000+00:00')
|
||||||
|
expect(subject.keys).to match_array(field_list)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'champs' do
|
describe 'champs' do
|
||||||
|
@ -250,11 +258,13 @@ describe API::V1::DossiersController do
|
||||||
}
|
}
|
||||||
subject { super()[:type_de_champ] }
|
subject { super()[:type_de_champ] }
|
||||||
|
|
||||||
it { expect(subject.key?(:id)).to be_truthy }
|
it do
|
||||||
it { expect(subject[:libelle]).to include('Libelle du champ') }
|
expect(subject.key?(:id)).to be_truthy
|
||||||
it { expect(subject[:description]).to include('description du champ') }
|
expect(subject[:libelle]).to include('Libelle du champ')
|
||||||
it { expect(subject.key?(:order_place)).to be_truthy }
|
expect(subject[:description]).to include('description du champ')
|
||||||
it { expect(subject[:type_champ]).to eq('text') }
|
expect(subject.key?(:order_place)).to be_truthy
|
||||||
|
expect(subject[:type_champ]).to eq('text')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -310,11 +320,13 @@ describe API::V1::DossiersController do
|
||||||
}
|
}
|
||||||
subject { super()[:type_de_champ] }
|
subject { super()[:type_de_champ] }
|
||||||
|
|
||||||
it { expect(subject.key?(:id)).to be_truthy }
|
it do
|
||||||
it { expect(subject[:libelle]).to include('Libelle champ privé') }
|
expect(subject.key?(:id)).to be_truthy
|
||||||
it { expect(subject[:description]).to include('description du champ privé') }
|
expect(subject[:libelle]).to include('Libelle champ privé')
|
||||||
it { expect(subject.key?(:order_place)).to be_truthy }
|
expect(subject[:description]).to include('description du champ privé')
|
||||||
it { expect(subject[:type_champ]).to eq('text') }
|
expect(subject.key?(:order_place)).to be_truthy
|
||||||
|
expect(subject[:type_champ]).to eq('text')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -325,11 +337,12 @@ describe API::V1::DossiersController do
|
||||||
|
|
||||||
subject { super()[:commentaires] }
|
subject { super()[:commentaires] }
|
||||||
|
|
||||||
it { expect(subject.size).to eq 2 }
|
it do
|
||||||
|
expect(subject.size).to eq 2
|
||||||
it { expect(subject.first[:body]).to eq 'plop' }
|
expect(subject.first[:body]).to eq 'plop'
|
||||||
it { expect(subject.first[:created_at]).to eq '2016-03-14T13:00:00.000Z' }
|
expect(subject.first[:created_at]).to eq '2016-03-14T13:00:00.000Z'
|
||||||
it { expect(subject.first[:email]).to eq 'plop@plip.com' }
|
expect(subject.first[:email]).to eq 'plop@plip.com'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'avis' do
|
describe 'avis' do
|
||||||
|
@ -359,19 +372,21 @@ describe API::V1::DossiersController do
|
||||||
}
|
}
|
||||||
subject { super()[:etablissement] }
|
subject { super()[:etablissement] }
|
||||||
|
|
||||||
it { expect(subject[:siret]).to eq('44011762001530') }
|
it do
|
||||||
it { expect(subject[:siege_social]).to eq(true) }
|
expect(subject[:siret]).to eq('44011762001530')
|
||||||
it { expect(subject[:naf]).to eq('4950Z') }
|
expect(subject[:siege_social]).to eq(true)
|
||||||
it { expect(subject[:libelle_naf]).to eq('Transports par conduites') }
|
expect(subject[:naf]).to eq('4950Z')
|
||||||
it { expect(subject[:adresse]).to eq("GRTGAZ\r IMMEUBLE BORA\r 6 RUE RAOUL NORDLING\r 92270 BOIS COLOMBES\r") }
|
expect(subject[:libelle_naf]).to eq('Transports par conduites')
|
||||||
it { expect(subject[:numero_voie]).to eq('6') }
|
expect(subject[:adresse]).to eq("GRTGAZ\r IMMEUBLE BORA\r 6 RUE RAOUL NORDLING\r 92270 BOIS COLOMBES\r")
|
||||||
it { expect(subject[:type_voie]).to eq('RUE') }
|
expect(subject[:numero_voie]).to eq('6')
|
||||||
it { expect(subject[:nom_voie]).to eq('RAOUL NORDLING') }
|
expect(subject[:type_voie]).to eq('RUE')
|
||||||
it { expect(subject[:complement_adresse]).to eq('IMMEUBLE BORA') }
|
expect(subject[:nom_voie]).to eq('RAOUL NORDLING')
|
||||||
it { expect(subject[:code_postal]).to eq('92270') }
|
expect(subject[:complement_adresse]).to eq('IMMEUBLE BORA')
|
||||||
it { expect(subject[:localite]).to eq('BOIS COLOMBES') }
|
expect(subject[:code_postal]).to eq('92270')
|
||||||
it { expect(subject[:code_insee_localite]).to eq('92009') }
|
expect(subject[:localite]).to eq('BOIS COLOMBES')
|
||||||
it { expect(subject.keys).to match_array(field_list) }
|
expect(subject[:code_insee_localite]).to eq('92009')
|
||||||
|
expect(subject.keys).to match_array(field_list)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,13 @@ describe API::V1::ProceduresController, type: :controller do
|
||||||
context 'when procedure belongs to administrateur without token' do
|
context 'when procedure belongs to administrateur without token' do
|
||||||
let(:procedure_id) { create(:procedure).id }
|
let(:procedure_id) { create(:procedure).id }
|
||||||
|
|
||||||
|
it { is_expected.to have_http_status(404) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when procedure exist but bad token' do
|
||||||
|
let(:token) { 'bad' }
|
||||||
|
let(:procedure_id) { create(:procedure, administrateur: admin).id }
|
||||||
|
|
||||||
it { is_expected.to have_http_status(401) }
|
it { is_expected.to have_http_status(401) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,25 +37,29 @@ describe API::V1::ProceduresController, type: :controller do
|
||||||
|
|
||||||
subject { JSON.parse(response.body, symbolize_names: true)[:procedure] }
|
subject { JSON.parse(response.body, symbolize_names: true)[:procedure] }
|
||||||
|
|
||||||
it { expect(subject[:id]).to eq(procedure.id) }
|
it do
|
||||||
it { expect(subject[:label]).to eq(procedure.libelle) }
|
expect(subject[:id]).to eq(procedure.id)
|
||||||
it { expect(subject[:description]).to eq(procedure.description) }
|
expect(subject[:label]).to eq(procedure.libelle)
|
||||||
it { expect(subject[:organisation]).to eq(procedure.organisation) }
|
expect(subject[:description]).to eq(procedure.description)
|
||||||
it { expect(subject[:archived_at]).to eq(procedure.closed_at) }
|
expect(subject[:organisation]).to eq(procedure.organisation)
|
||||||
it { expect(subject[:direction]).to eq("") }
|
expect(subject[:archived_at]).to eq(procedure.closed_at)
|
||||||
it { expect(subject[:total_dossier]).to eq(procedure.total_dossier) }
|
expect(subject[:direction]).to eq("")
|
||||||
it { is_expected.to have_key(:types_de_champ) }
|
expect(subject[:total_dossier]).to eq(procedure.total_dossier)
|
||||||
it { expect(subject[:types_de_champ]).to be_an(Array) }
|
is_expected.to have_key(:types_de_champ)
|
||||||
|
expect(subject[:types_de_champ]).to be_an(Array)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'type_de_champ' do
|
describe 'type_de_champ' do
|
||||||
subject { super()[:types_de_champ][0] }
|
subject { super()[:types_de_champ][0] }
|
||||||
|
|
||||||
let(:champ) { procedure.active_revision.types_de_champ_public.first }
|
let(:champ) { procedure.active_revision.types_de_champ_public.first }
|
||||||
|
|
||||||
it { expect(subject[:id]).to eq(champ.id) }
|
it do
|
||||||
it { expect(subject[:libelle]).to eq(champ.libelle) }
|
expect(subject[:id]).to eq(champ.id)
|
||||||
it { expect(subject[:type_champ]).to eq(champ.type_champ) }
|
expect(subject[:libelle]).to eq(champ.libelle)
|
||||||
it { expect(subject[:description]).to eq(champ.description) }
|
expect(subject[:type_champ]).to eq(champ.type_champ)
|
||||||
|
expect(subject[:description]).to eq(champ.description)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'service' do
|
describe 'service' do
|
||||||
|
@ -56,14 +67,16 @@ describe API::V1::ProceduresController, type: :controller do
|
||||||
|
|
||||||
let(:service) { procedure.service }
|
let(:service) { procedure.service }
|
||||||
|
|
||||||
it { expect(subject[:id]).to eq(service.id) }
|
it do
|
||||||
it { expect(subject[:email]).to eq(service.email) }
|
expect(subject[:id]).to eq(service.id)
|
||||||
it { expect(subject[:name]).to eq(service.nom) }
|
expect(subject[:email]).to eq(service.email)
|
||||||
it { expect(subject[:type_organization]).to eq(service.type_organisme) }
|
expect(subject[:name]).to eq(service.nom)
|
||||||
it { expect(subject[:organization]).to eq(service.organisme) }
|
expect(subject[:type_organization]).to eq(service.type_organisme)
|
||||||
it { expect(subject[:phone]).to eq(service.telephone) }
|
expect(subject[:organization]).to eq(service.organisme)
|
||||||
it { expect(subject[:schedule]).to eq(service.horaires) }
|
expect(subject[:phone]).to eq(service.telephone)
|
||||||
it { expect(subject[:address]).to eq(service.adresse) }
|
expect(subject[:schedule]).to eq(service.horaires)
|
||||||
|
expect(subject[:address]).to eq(service.adresse)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,6 @@ describe API::V2::GraphqlController do
|
||||||
let(:generated_token) { APIToken.generate(admin) }
|
let(:generated_token) { APIToken.generate(admin) }
|
||||||
let(:api_token) { generated_token.first }
|
let(:api_token) { generated_token.first }
|
||||||
let(:token) { generated_token.second }
|
let(:token) { generated_token.second }
|
||||||
let(:legacy_token) { APIToken.send(:unpack, token)[:plain_token] }
|
|
||||||
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, administrateurs: [admin]) }
|
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, administrateurs: [admin]) }
|
||||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
|
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
|
||||||
let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure: procedure, en_construction_at: 1.day.ago) }
|
let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure: procedure, en_construction_at: 1.day.ago) }
|
||||||
|
@ -113,20 +112,6 @@ describe API::V2::GraphqlController do
|
||||||
|
|
||||||
subject { post :execute, params: { query: query, variables: variables, operationName: operation_name, queryId: query_id }.compact, as: :json }
|
subject { post :execute, params: { query: query, variables: variables, operationName: operation_name, queryId: query_id }.compact, as: :json }
|
||||||
|
|
||||||
context "when authenticated with legacy token" do
|
|
||||||
let(:authorization_header) { ActionController::HttpAuthentication::Token.encode_credentials(legacy_token) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
request.env['HTTP_AUTHORIZATION'] = authorization_header
|
|
||||||
admin.api_tokens.first.update(version: 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the demarche" do
|
|
||||||
expect(gql_errors).to eq(nil)
|
|
||||||
expect(gql_data[:demarche][:id]).to eq(procedure.to_typed_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when authenticated" do
|
context "when authenticated" do
|
||||||
let(:authorization_header) { ActionController::HttpAuthentication::Token.encode_credentials(token) }
|
let(:authorization_header) { ActionController::HttpAuthentication::Token.encode_credentials(token) }
|
||||||
|
|
||||||
|
@ -164,18 +149,6 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
|
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
context 'v2' do
|
|
||||||
let(:token) { APIToken.send(:message_verifier).generate([another_administrateur.id, plain_token]) }
|
|
||||||
it {
|
|
||||||
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
|
|
||||||
}
|
|
||||||
end
|
|
||||||
context 'v1' do
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it {
|
|
||||||
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the token is revoked" do
|
context "when the token is revoked" do
|
||||||
|
@ -1499,13 +1472,15 @@ describe API::V2::GraphqlController do
|
||||||
message {
|
message {
|
||||||
body
|
body
|
||||||
}
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return error" do
|
it "should return error" do
|
||||||
expect(gql_data[:dossierEnvoyerMessage]).to eq(nil)
|
expect(gql_data[:dossierEnvoyerMessage][:errors].first[:message]).to eq("Le jeton utilisé est configuré seulement en lecture")
|
||||||
expect(gql_errors).not_to eq(nil)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,36 +1,41 @@
|
||||||
describe APIController, type: :controller do
|
describe APIController, type: :controller do
|
||||||
describe 'valid_token_for_procedure?' do
|
describe 'authenticate_from_token' do
|
||||||
let(:procedure) { create(:procedure) }
|
let(:procedure) { create(:procedure) }
|
||||||
let(:admin) { procedure.administrateurs.first }
|
let(:admin) { procedure.administrateurs.first }
|
||||||
|
|
||||||
subject { !!controller.send(:find_administrateur_for_token, procedure) }
|
subject do
|
||||||
|
controller.send(:authenticate_from_token)
|
||||||
|
assigns(:api_token)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when the admin has not any token' do
|
context 'when the admin has not any token' do
|
||||||
context 'and the token is not given' do
|
context 'and the token is not given' do
|
||||||
it { is_expected.to be false }
|
it { is_expected.to be nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the admin has a token' do
|
context 'when the admin has a token' do
|
||||||
let!(:token) { APIToken.generate(admin)[1] }
|
let(:token_bearer_couple) { APIToken.generate(admin) }
|
||||||
|
let(:token) { token_bearer_couple[0] }
|
||||||
|
let(:bearer) { token_bearer_couple[1] }
|
||||||
|
|
||||||
context 'and the token is given by params' do
|
context 'and the token is given by params' do
|
||||||
before { controller.params[:token] = token }
|
before { controller.params[:token] = bearer }
|
||||||
|
|
||||||
it { is_expected.to be true }
|
it { is_expected.to eq(token) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'and the token is given by header' do
|
context 'and the token is given by header' do
|
||||||
before do
|
before do
|
||||||
valid_headers = { 'Authorization' => "Bearer token=#{token}" }
|
valid_headers = { 'Authorization' => "Bearer token=#{bearer}" }
|
||||||
request.headers.merge!(valid_headers)
|
request.headers.merge!(valid_headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to be true }
|
it { is_expected.to eq(token) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'and the token is not given' do
|
context 'and the token is not given' do
|
||||||
it { is_expected.to be false }
|
it { is_expected.to be nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,245 +1,160 @@
|
||||||
describe APIToken, type: :model do
|
describe APIToken, type: :model do
|
||||||
let(:administrateur) { create(:administrateur) }
|
let(:administrateur) { create(:administrateur) }
|
||||||
let(:api_token_and_packed_token) { APIToken.generate(administrateur) }
|
|
||||||
let(:api_token) { api_token_and_packed_token.first }
|
|
||||||
let(:packed_token) { api_token_and_packed_token.second }
|
|
||||||
let(:plain_token) { APIToken.send(:unpack, packed_token)[:plain_token] }
|
|
||||||
let(:packed_token_v2) { APIToken.send(:message_verifier).generate([administrateur.id, plain_token]) }
|
|
||||||
|
|
||||||
describe '#generate' do
|
describe '#generate' do
|
||||||
it do
|
let(:api_token_and_packed_token) { APIToken.generate(administrateur) }
|
||||||
|
let(:api_token) { api_token_and_packed_token.first }
|
||||||
|
let(:packed_token) { api_token_and_packed_token.second }
|
||||||
|
|
||||||
|
before { api_token_and_packed_token }
|
||||||
|
|
||||||
|
it 'with a full access token' do
|
||||||
expect(api_token.administrateur).to eq(administrateur)
|
expect(api_token.administrateur).to eq(administrateur)
|
||||||
expect(api_token.prefix).to eq(packed_token.slice(0, 5))
|
expect(api_token.prefix).to eq(packed_token.slice(0, 5))
|
||||||
expect(api_token.version).to eq(3)
|
expect(api_token.version).to eq(3)
|
||||||
expect(api_token.write_access?).to eq(true)
|
expect(api_token.write_access?).to eq(true)
|
||||||
expect(api_token.procedure_ids).to eq([])
|
expect(api_token.procedure_ids).to eq([])
|
||||||
expect(api_token.allowed_procedure_ids).to eq(nil)
|
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
||||||
expect(api_token.full_access?).to be_truthy
|
expect(api_token.full_access?).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with read_only' do
|
context 'updated read_only' do
|
||||||
before { api_token.update(write_access: false) }
|
before { api_token.update(write_access: false) }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect(api_token.full_access?).to be_truthy
|
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: false)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with procedure' do
|
context 'with a new added procedure' do
|
||||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||||
before { procedure }
|
|
||||||
|
before do
|
||||||
|
procedure
|
||||||
|
api_token.reload
|
||||||
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect(api_token.full_access?).to be_truthy
|
||||||
expect(api_token.procedure_ids).to eq([procedure.id])
|
expect(api_token.procedure_ids).to eq([procedure.id])
|
||||||
expect(api_token.procedures_to_allow).to eq([procedure])
|
expect(api_token.procedures).to eq([procedure])
|
||||||
expect(api_token.allowed_procedure_ids).to eq(nil)
|
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [procedure.id], write_access: true)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [procedure.id], write_access: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'update with procedure_id' do
|
context 'and another procedure, but access only to the first one' do
|
||||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
|
||||||
let(:other_procedure) { create(:procedure, administrateurs: [administrateur]) }
|
let(:other_procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||||
before { api_token.update(allowed_procedure_ids: [procedure.id]); other_procedure }
|
|
||||||
|
before do
|
||||||
|
other_procedure
|
||||||
|
api_token.update(allowed_procedure_ids: [procedure.id])
|
||||||
|
api_token.reload
|
||||||
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect(api_token.procedure_ids).to match_array([procedure.id, other_procedure.id])
|
expect(api_token.full_access?).to be_falsey
|
||||||
expect(api_token.procedures_to_allow).to eq([other_procedure])
|
expect(api_token.procedure_ids).to match_array([procedure.id])
|
||||||
expect(api_token.allowed_procedure_ids).to eq([procedure.id])
|
expect(api_token.targetable_procedures).to eq([other_procedure])
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [procedure.id], write_access: true)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [procedure.id], write_access: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'and then gain full access' do
|
||||||
|
before do
|
||||||
|
api_token.become_full_access!
|
||||||
|
api_token.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
expect(api_token.full_access?).to be(true)
|
||||||
|
expect(api_token.procedure_ids).to match_array([procedure.id, other_procedure.id])
|
||||||
|
expect(api_token.targetable_procedures).to eq([procedure, other_procedure])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'update with wrong procedure_id' do
|
context 'but acces to a wrong procedure_id' do
|
||||||
let(:other_administrateur) { create(:administrateur) }
|
let(:forbidden_procedure) { create(:procedure) }
|
||||||
let(:procedure) { create(:procedure, administrateurs: [other_administrateur]) }
|
|
||||||
before { api_token.update(allowed_procedure_ids: [procedure.id]) }
|
before do
|
||||||
|
api_token.update(allowed_procedure_ids: [forbidden_procedure.id])
|
||||||
|
api_token.reload
|
||||||
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect(api_token.full_access?).to be_falsey
|
expect(api_token.full_access?).to be_falsey
|
||||||
expect(api_token.procedure_ids).to eq([])
|
expect(api_token.procedure_ids).to eq([])
|
||||||
expect(api_token.procedures_to_allow).to eq([])
|
expect(api_token.targetable_procedures).to eq([procedure])
|
||||||
expect(api_token.allowed_procedure_ids).to eq([])
|
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'update with destroyed procedure_id' do
|
context 'update with destroyed procedure_id' do
|
||||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||||
before { api_token.update(allowed_procedure_ids: [procedure.id]); procedure.destroy }
|
|
||||||
|
before do
|
||||||
|
api_token.update(allowed_procedure_ids: [procedure.id])
|
||||||
|
procedure.destroy
|
||||||
|
api_token.reload
|
||||||
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect(api_token.full_access?).to be_falsey
|
expect(api_token.full_access?).to be_falsey
|
||||||
expect(api_token.procedure_ids).to eq([])
|
expect(api_token.procedure_ids).to eq([])
|
||||||
expect(api_token.procedures_to_allow).to eq([])
|
expect(api_token.targetable_procedures).to eq([])
|
||||||
expect(api_token.allowed_procedure_ids).to eq([procedure.id])
|
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'update with detached procedure_id' do
|
context 'update with detached procedure_id' do
|
||||||
let(:other_procedure) { create(:procedure, administrateurs: [administrateur]) }
|
|
||||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||||
before { api_token.update(allowed_procedure_ids: [procedure.id]); other_procedure; administrateur.procedures.delete(procedure) }
|
let(:other_procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
api_token.update(allowed_procedure_ids: [procedure.id])
|
||||||
|
other_procedure
|
||||||
|
administrateur.procedures.delete(procedure)
|
||||||
|
api_token.reload
|
||||||
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect(api_token.full_access?).to be_falsey
|
expect(api_token.full_access?).to be_falsey
|
||||||
expect(api_token.procedure_ids).to eq([other_procedure.id])
|
expect(api_token.procedure_ids).to eq([])
|
||||||
expect(api_token.allowed_procedure_ids).to eq([procedure.id])
|
expect(api_token.targetable_procedures).to eq([other_procedure])
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with procedure and allowed_procedure_ids' do
|
|
||||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
|
||||||
let(:other_procedure) { create(:procedure, administrateurs: [administrateur]) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
api_token.update(allowed_procedure_ids: [procedure.id])
|
|
||||||
other_procedure
|
|
||||||
end
|
|
||||||
|
|
||||||
it do
|
|
||||||
expect(api_token.procedure_ids).to match_array([procedure.id, other_procedure.id])
|
|
||||||
expect(api_token.allowed_procedure_ids).to eq([procedure.id])
|
|
||||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [procedure.id], write_access: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#find_and_verify' do
|
describe '#authenticate' do
|
||||||
let(:result) { APIToken.find_and_verify(token, administrateurs) }
|
let(:api_token_and_packed_token) { APIToken.generate(administrateur) }
|
||||||
let(:token) { packed_token }
|
let(:api_token) { api_token_and_packed_token.first }
|
||||||
let(:administrateurs) { [administrateur] }
|
let(:packed_token) { api_token_and_packed_token.second }
|
||||||
|
let(:bearer_token) { packed_token }
|
||||||
|
|
||||||
context 'without administrateur' do
|
subject { APIToken.authenticate(bearer_token) }
|
||||||
let(:administrateurs) { [] }
|
|
||||||
|
|
||||||
context 'with packed token' do
|
context 'with the legit packed token' do
|
||||||
it { expect(result).to be_truthy }
|
it { is_expected.to eq(api_token) }
|
||||||
end
|
|
||||||
|
|
||||||
context 'with packed token v2' do
|
|
||||||
before { api_token.update(version: 2) }
|
|
||||||
let(:token) { packed_token_v2 }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with plain token' do
|
|
||||||
before { api_token.update(version: 1) }
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with destroyed token' do
|
context 'with destroyed token' do
|
||||||
before { api_token.destroy }
|
before { api_token.destroy }
|
||||||
|
|
||||||
context 'with packed token' do
|
it { is_expected.to be_nil }
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with packed token v2' do
|
|
||||||
let(:token) { packed_token_v2 }
|
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with plain token' do
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with destroyed administrateur' do
|
context 'with destroyed administrateur' do
|
||||||
before { api_token.administrateur.destroy }
|
before { api_token.administrateur.destroy }
|
||||||
let(:administrateurs) { [] }
|
|
||||||
|
|
||||||
context 'with packed token' do
|
it { is_expected.to be_nil }
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with packed token v2' do
|
|
||||||
let(:token) { packed_token_v2 }
|
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with plain token' do
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with other administrateur' do
|
context "with a bearer token with the wrong plain_token" do
|
||||||
let(:other_administrateur) { create(:administrateur, :with_api_token) }
|
let(:bearer_token) do
|
||||||
let(:administrateurs) { [other_administrateur] }
|
APIToken::BearerToken.new(api_token.id, 'wrong').to_string
|
||||||
|
|
||||||
context 'with packed token' do
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with packed token v2' do
|
it { is_expected.to be_nil }
|
||||||
before { api_token.update(version: 2) }
|
|
||||||
|
|
||||||
let(:token) { packed_token_v2 }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with plain token' do
|
|
||||||
before { api_token.update(version: 1) }
|
|
||||||
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it { expect(result).to be_falsey }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with many administrateurs' do
|
|
||||||
let(:other_administrateur) { create(:administrateur, :with_api_token) }
|
|
||||||
let(:other_api_token_and_packed_token) { APIToken.generate(other_administrateur) }
|
|
||||||
let(:other_api_token) { other_api_token_and_packed_token.first }
|
|
||||||
let(:other_packed_token) { other_api_token_and_packed_token.second }
|
|
||||||
let(:other_plain_token) { APIToken.send(:unpack, other_packed_token)[:plain_token] }
|
|
||||||
let(:administrateurs) { [administrateur, other_administrateur] }
|
|
||||||
|
|
||||||
context 'with plain token' do
|
|
||||||
before do
|
|
||||||
api_token.update(version: 1)
|
|
||||||
other_api_token.update(version: 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
|
|
||||||
context 'with other plain token' do
|
|
||||||
let(:token) { other_plain_token }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with packed token' do
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with packed token v2' do
|
|
||||||
before { api_token.update(version: 2) }
|
|
||||||
|
|
||||||
let(:token) { packed_token_v2 }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with plain token' do
|
|
||||||
before { api_token.update(version: 1) }
|
|
||||||
|
|
||||||
let(:token) { plain_token }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with valid garbage base64" do
|
|
||||||
before { api_token.update(version: 1, encrypted_token: BCrypt::Password.create(token)) }
|
|
||||||
|
|
||||||
let(:token) { "R5dAqE7nMxfMp93PcuuevDtn" }
|
|
||||||
it { expect(result).to be_truthy }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue