refactor of api_token
This commit is contained in:
parent
24fd12ed70
commit
05a8fd8ee1
2 changed files with 100 additions and 68 deletions
|
@ -2,42 +2,53 @@ class APIToken < ApplicationRecord
|
|||
include ActiveRecord::SecureToken
|
||||
|
||||
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
|
||||
context = { administrateur_id: administrateur_id, write_access: write_access? }
|
||||
{
|
||||
administrateur_id:,
|
||||
procedure_ids:,
|
||||
write_access:
|
||||
}
|
||||
end
|
||||
|
||||
def procedure_ids
|
||||
if full_access?
|
||||
context.merge procedure_ids:
|
||||
administrateur.procedures.ids
|
||||
else
|
||||
context.merge procedure_ids: procedure_ids & allowed_procedure_ids
|
||||
sanitized_targeted_procedure_ids
|
||||
end
|
||||
end
|
||||
|
||||
def procedures
|
||||
Procedure.where(id: procedure_ids)
|
||||
end
|
||||
|
||||
def full_access?
|
||||
allowed_procedure_ids.nil?
|
||||
targeted_procedure_ids.nil?
|
||||
end
|
||||
|
||||
def procedures_to_allow
|
||||
procedures.select(:id, :libelle, :path).where.not(id: allowed_procedure_ids || []).order(:libelle)
|
||||
def targetable_procedures
|
||||
administrateur
|
||||
.procedures
|
||||
.where.not(id: targeted_procedure_ids)
|
||||
.select(:id, :libelle, :path)
|
||||
.order(:libelle)
|
||||
end
|
||||
|
||||
def allowed_procedures
|
||||
if allowed_procedure_ids.present?
|
||||
procedures.select(:id, :libelle, :path).where(id: allowed_procedure_ids).order(:libelle)
|
||||
else
|
||||
[]
|
||||
end
|
||||
def untarget_procedure(procedure_id)
|
||||
new_target_ids = targeted_procedure_ids - [procedure_id]
|
||||
|
||||
update!(allowed_procedure_ids: new_target_ids)
|
||||
end
|
||||
|
||||
def disallow_procedure(procedure_id)
|
||||
allowed_procedure_ids = allowed_procedures.map(&:id) - [procedure_id]
|
||||
if allowed_procedure_ids.empty?
|
||||
allowed_procedure_ids = nil
|
||||
end
|
||||
update!(allowed_procedure_ids:)
|
||||
def sanitized_targeted_procedure_ids
|
||||
administrateur.procedures.ids.intersection(targeted_procedure_ids || [])
|
||||
end
|
||||
|
||||
def become_full_access!
|
||||
update_column(:allowed_procedure_ids, nil)
|
||||
end
|
||||
|
||||
# Prefix is made of the first 6 characters of the uuid base64 encoded
|
||||
|
@ -70,12 +81,16 @@ class APIToken < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def check_allowed_procedure_ids_ownership
|
||||
if allowed_procedure_ids.present?
|
||||
self.allowed_procedure_ids = allowed_procedures.map(&:id)
|
||||
def sanitize_targeted_procedure_ids
|
||||
if targeted_procedure_ids.present?
|
||||
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(';'))
|
||||
|
|
|
@ -6,105 +6,123 @@ describe APIToken, type: :model do
|
|||
let(:api_token) { api_token_and_packed_token.first }
|
||||
let(:packed_token) { api_token_and_packed_token.second }
|
||||
|
||||
it do
|
||||
before { api_token_and_packed_token }
|
||||
|
||||
it 'with a full access token' 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
|
||||
context 'updated 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
|
||||
context 'with a new added procedure' do
|
||||
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||
before { procedure }
|
||||
|
||||
before do
|
||||
procedure
|
||||
api_token.reload
|
||||
end
|
||||
|
||||
it do
|
||||
expect(api_token.full_access?).to be_truthy
|
||||
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.procedures).to eq([procedure])
|
||||
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]) }
|
||||
context 'and another procedure, but access only to the first one' do
|
||||
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
|
||||
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.full_access?).to be_falsey
|
||||
expect(api_token.procedure_ids).to match_array([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)
|
||||
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
|
||||
|
||||
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]) }
|
||||
context 'but acces to a wrong procedure_id' do
|
||||
let(:forbidden_procedure) { create(:procedure) }
|
||||
|
||||
before do
|
||||
api_token.update(allowed_procedure_ids: [forbidden_procedure.id])
|
||||
api_token.reload
|
||||
end
|
||||
|
||||
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.targetable_procedures).to eq([procedure])
|
||||
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 }
|
||||
|
||||
before do
|
||||
api_token.update(allowed_procedure_ids: [procedure.id])
|
||||
procedure.destroy
|
||||
api_token.reload
|
||||
end
|
||||
|
||||
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.targetable_procedures).to eq([])
|
||||
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) }
|
||||
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
|
||||
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.procedure_ids).to eq([])
|
||||
expect(api_token.targetable_procedures).to eq([other_procedure])
|
||||
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 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
|
||||
|
||||
describe '#authenticate' do
|
||||
|
@ -133,8 +151,7 @@ describe APIToken, type: :model do
|
|||
|
||||
context "with a bearer token with the wrong plain_token" do
|
||||
let(:bearer_token) do
|
||||
clear_packed = [api_token.id, 'wrong'].join(';')
|
||||
Base64.urlsafe_encode64(clear_packed)
|
||||
APIToken::BearerToken.new(api_token.id, 'wrong').to_string
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
|
|
Loading…
Reference in a new issue