feat(api_token): add allowed_procedure_ids and write_access
This commit is contained in:
parent
85d31632e3
commit
8ee13f1719
9 changed files with 173 additions and 15 deletions
|
@ -10,13 +10,20 @@ class API::V2::BaseController < ApplicationController
|
|||
def context
|
||||
# new token
|
||||
if api_token.present?
|
||||
{ administrateur_id: api_token.administrateur.id }
|
||||
api_token.context
|
||||
# web interface (/graphql) give current_administrateur
|
||||
elsif current_administrateur.present?
|
||||
{ administrateur_id: current_administrateur.id }
|
||||
{
|
||||
administrateur_id: current_administrateur.id,
|
||||
procedure_ids: current_administrateur.procedure_ids,
|
||||
write_access: true
|
||||
}
|
||||
# old token
|
||||
else
|
||||
{ token: authorization_bearer_token }
|
||||
{
|
||||
token: authorization_bearer_token,
|
||||
write_access: true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class APIController < ApplicationController
|
|||
|
||||
def find_administrateur_for_token(procedure)
|
||||
api_token = APIToken.find_and_verify(authorization_bearer_token, procedure.administrateurs)
|
||||
if api_token.present? && procedure.administrateurs.include?(api_token.administrateur)
|
||||
if api_token.present? && api_token.context.fetch(:procedure_ids).include?(procedure.id)
|
||||
api_token.administrateur
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,10 +49,12 @@ class API::V2::Context < GraphQL::Query::Context
|
|||
self[:authorized] ||= {}
|
||||
|
||||
if self[:authorized][demarche.id].nil?
|
||||
self[:authorized][demarche.id] = if self[:administrateur_id]
|
||||
demarche.administrateurs.map(&:id).include?(self[:administrateur_id])
|
||||
elsif self[:token]
|
||||
self[:authorized][demarche.id] = if self[:procedure_ids].present?
|
||||
self[:procedure_ids].include?(demarche.id)
|
||||
elsif self[:token].present?
|
||||
APIToken.find_and_verify(self[:token], demarche.administrateurs).present?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -16,6 +16,17 @@ class APIToken < ApplicationRecord
|
|||
include ActiveRecord::SecureToken
|
||||
|
||||
belongs_to :administrateur, inverse_of: :api_tokens
|
||||
has_many :procedures, through: :administrateur
|
||||
|
||||
def context
|
||||
context = { administrateur_id: administrateur_id, write_access: write_access? }
|
||||
|
||||
if full_access?
|
||||
context.merge procedure_ids:
|
||||
else
|
||||
context.merge procedure_ids: procedure_ids & allowed_procedure_ids
|
||||
end
|
||||
end
|
||||
|
||||
# Prefix is made of the first 6 characters of the uuid base64 encoded
|
||||
# it does not leak plain token
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
describe API::V2::GraphqlController do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:token) { APIToken.generate(admin)[1] }
|
||||
let(:generated_token) { APIToken.generate(admin) }
|
||||
let(:api_token) { generated_token.first }
|
||||
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(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
|
||||
|
@ -193,6 +195,15 @@ describe API::V2::GraphqlController do
|
|||
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
|
||||
}
|
||||
end
|
||||
|
||||
context 'when procedure is not selected' do
|
||||
let(:other_procedure) { create(:procedure, administrateurs: [admin]) }
|
||||
before { api_token.update(allowed_procedure_ids: [other_procedure.id]) }
|
||||
|
||||
it {
|
||||
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "demarche" do
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
describe API::V2::GraphqlController do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:token) { APIToken.generate(admin)[1] }
|
||||
let(:generated_token) { APIToken.generate(admin) }
|
||||
let(:api_token) { generated_token.first }
|
||||
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], types_de_champ_public:) }
|
||||
let(:types_de_champ_public) { [] }
|
||||
|
@ -210,6 +212,14 @@ describe API::V2::GraphqlController do
|
|||
expect(gql_data[:dossierArchiver][:dossier][:id]).to eq(dossier.to_typed_id)
|
||||
expect(gql_data[:dossierArchiver][:dossier][:archived]).to be_truthy
|
||||
}
|
||||
|
||||
context 'read only token' do
|
||||
before { api_token.update(write_access: false) }
|
||||
|
||||
it {
|
||||
expect(gql_data[:dossierArchiver][:errors].first[:message]).to eq('Le jeton utilisé est configuré seulement en lecture')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'dossierPasserEnInstruction' do
|
||||
|
@ -262,6 +272,14 @@ describe API::V2::GraphqlController do
|
|||
expect(gql_data[:dossierAccepter][:dossier][:id]).to eq(dossier.to_typed_id)
|
||||
expect(gql_data[:dossierAccepter][:dossier][:state]).to eq('accepte')
|
||||
}
|
||||
|
||||
context 'read only token' do
|
||||
before { api_token.update(write_access: false) }
|
||||
|
||||
it {
|
||||
expect(gql_data[:dossierAccepter][:errors].first[:message]).to eq('Le jeton utilisé est configuré seulement en lecture')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'dossierRefuser' do
|
||||
|
@ -275,6 +293,14 @@ describe API::V2::GraphqlController do
|
|||
expect(gql_data[:dossierRefuser][:dossier][:id]).to eq(dossier.to_typed_id)
|
||||
expect(gql_data[:dossierRefuser][:dossier][:state]).to eq('refuse')
|
||||
}
|
||||
|
||||
context 'read only token' do
|
||||
before { api_token.update(write_access: false) }
|
||||
|
||||
it {
|
||||
expect(gql_data[:dossierRefuser][:errors].first[:message]).to eq('Le jeton utilisé est configuré seulement en lecture')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'dossierClasserSansSuite' do
|
||||
|
@ -288,6 +314,14 @@ describe API::V2::GraphqlController do
|
|||
expect(gql_data[:dossierClasserSansSuite][:dossier][:id]).to eq(dossier.to_typed_id)
|
||||
expect(gql_data[:dossierClasserSansSuite][:dossier][:state]).to eq('sans_suite')
|
||||
}
|
||||
|
||||
context 'read only token' do
|
||||
before { api_token.update(write_access: false) }
|
||||
|
||||
it {
|
||||
expect(gql_data[:dossierClasserSansSuite][:errors].first[:message]).to eq('Le jeton utilisé est configuré seulement en lecture')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'groupeInstructeurModifier' do
|
||||
|
|
|
@ -5,7 +5,7 @@ RSpec.describe Mutations::DossierModifierAnnotation, type: :graphql do
|
|||
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
|
||||
|
||||
let(:query) { '' }
|
||||
let(:context) { { administrateur_id: admin.id } }
|
||||
let(:context) { { administrateur_id: admin.id, procedure_ids: admin.procedure_ids, write_access: true } }
|
||||
let(:variables) { {} }
|
||||
|
||||
subject { API::V2::Schema.execute(query, variables: variables, context: context) }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
RSpec.describe Types::DemarcheType, type: :graphql do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:query) { '' }
|
||||
let(:context) { { internal_use: true } }
|
||||
let(:context) { { procedure_ids: admin.procedure_ids } }
|
||||
let(:variables) { {} }
|
||||
|
||||
subject { API::V2::Schema.execute(query, variables: variables, context: context) }
|
||||
|
@ -10,11 +11,9 @@ RSpec.describe Types::DemarcheType, type: :graphql do
|
|||
|
||||
describe 'context should correctly preserve demarche authorization state' do
|
||||
let(:query) { DEMARCHE_QUERY }
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, administrateurs: [admin]) }
|
||||
|
||||
let(:other_admin_procedure) { create(:procedure) }
|
||||
let(:context) { { administrateur_id: admin.id } }
|
||||
let(:variables) { { number: procedure.id } }
|
||||
|
||||
it do
|
||||
|
@ -27,8 +26,8 @@ RSpec.describe Types::DemarcheType, type: :graphql do
|
|||
end
|
||||
|
||||
describe 'demarche with clone' do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :yes_no }]) }
|
||||
let(:procedure_clone) { procedure.clone(procedure.administrateurs.first, false) }
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :yes_no }], administrateurs: [admin]) }
|
||||
let(:procedure_clone) { procedure.clone(admin, false) }
|
||||
let(:query) { DEMARCHE_WITH_CHAMP_DESCRIPTORS_QUERY }
|
||||
let(:variables) { { number: procedure_clone.id } }
|
||||
let(:champ_descriptor_id) { procedure.draft_revision.types_de_champ_public.first.to_typed_id }
|
||||
|
|
|
@ -11,6 +11,100 @@ describe APIToken, type: :model do
|
|||
expect(api_token.administrateur).to eq(administrateur)
|
||||
expect(api_token.prefix).to eq(packed_token.slice(0, 5))
|
||||
expect(api_token.version).to eq(3)
|
||||
expect(api_token.write_access?).to eq(true)
|
||||
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.full_access?).to be_truthy
|
||||
end
|
||||
|
||||
context 'with read_only' do
|
||||
before { api_token.update(write_access: false) }
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with procedure' do
|
||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||
before { procedure }
|
||||
|
||||
it do
|
||||
expect(api_token.procedure_ids).to eq([procedure.id])
|
||||
expect(api_token.procedures_to_allow).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)
|
||||
end
|
||||
|
||||
context 'update with procedure_id' do
|
||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||
let(:other_procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||
before { api_token.update(allowed_procedure_ids: [procedure.id]); other_procedure }
|
||||
|
||||
it do
|
||||
expect(api_token.procedure_ids).to match_array([procedure.id, other_procedure.id])
|
||||
expect(api_token.procedures_to_allow).to eq([other_procedure])
|
||||
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
|
||||
|
||||
context 'update with wrong procedure_id' do
|
||||
let(:other_administrateur) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, administrateurs: [other_administrateur]) }
|
||||
before { api_token.update(allowed_procedure_ids: [procedure.id]) }
|
||||
|
||||
it do
|
||||
expect(api_token.full_access?).to be_falsey
|
||||
expect(api_token.procedure_ids).to eq([])
|
||||
expect(api_token.procedures_to_allow).to eq([])
|
||||
expect(api_token.allowed_procedure_ids).to eq([])
|
||||
expect(api_token.context).to eq(administrateur_id: administrateur.id, procedure_ids: [], write_access: true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'update with destroyed procedure_id' do
|
||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||
before { api_token.update(allowed_procedure_ids: [procedure.id]); procedure.destroy }
|
||||
|
||||
it do
|
||||
expect(api_token.full_access?).to be_falsey
|
||||
expect(api_token.procedure_ids).to eq([])
|
||||
expect(api_token.procedures_to_allow).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)
|
||||
end
|
||||
end
|
||||
|
||||
context 'update with detached procedure_id' do
|
||||
let(:other_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) }
|
||||
|
||||
it do
|
||||
expect(api_token.full_access?).to be_falsey
|
||||
expect(api_token.procedure_ids).to eq([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: [], write_access: true)
|
||||
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 eq([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
|
||||
|
||||
|
|
Loading…
Reference in a new issue