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
|
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
|
||||||
|
@ -70,12 +81,16 @@ class APIToken < ApplicationRecord
|
||||||
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def targeted_procedure_ids
|
||||||
|
read_attribute(:allowed_procedure_ids)
|
||||||
|
end
|
||||||
|
|
||||||
class BearerToken < Data.define(:api_token_id, :plain_token)
|
class BearerToken < Data.define(:api_token_id, :plain_token)
|
||||||
def to_string
|
def to_string
|
||||||
Base64.urlsafe_encode64([api_token_id, plain_token].join(';'))
|
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(:api_token) { api_token_and_packed_token.first }
|
||||||
let(:packed_token) { api_token_and_packed_token.second }
|
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.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 '#authenticate' do
|
describe '#authenticate' do
|
||||||
|
@ -133,8 +151,7 @@ describe APIToken, type: :model do
|
||||||
|
|
||||||
context "with a bearer token with the wrong plain_token" do
|
context "with a bearer token with the wrong plain_token" do
|
||||||
let(:bearer_token) do
|
let(:bearer_token) do
|
||||||
clear_packed = [api_token.id, 'wrong'].join(';')
|
APIToken::BearerToken.new(api_token.id, 'wrong').to_string
|
||||||
Base64.urlsafe_encode64(clear_packed)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to be_nil }
|
it { is_expected.to be_nil }
|
||||||
|
|
Loading…
Reference in a new issue