Slide 1

Slide 1 text

WELCOME Vladimir Dementyev or access denied?

Slide 2

Slide 2 text

AUTHORIZATION авторизация povolení autorització 認可 упълномощаване Genehmigung овлашћење авторлык аўтарызацыя дозвіл танылған autoryzacja !"#$%&'!(&!

Slide 3

Slide 3 text

AUTHORIZATION or naming is hard palkan_tula palkan 3

Slide 4

Slide 4 text

I’M USING DEVISE FOR AUTHORIZATION!

Slide 5

Slide 5 text

palkan_tula palkan AUTHORIZATION Who’s there? 5 AUTHENTICATION ТУК-ТУК! КТО ТАМ? * — Knock-knock! — Who’s there?

Slide 6

Slide 6 text

palkan_tula palkan Am I allowed to do that? 6 AUTHORIZATION

Slide 7

Slide 7 text

HELLO

Slide 8

Slide 8 text

palkan_tula palkan VLADIMIR DEMENTYEV 8 " Brooklyn # Moscow $

Slide 9

Slide 9 text

palkan_tula palkan 9

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

palkan_tula palkan evl.ms/blog 11

Slide 12

Slide 12 text

palkan_tula palkan 12 @palkan @palkan_tula Vladimir Dementyev 1.1k 836 456 650

Slide 13

Slide 13 text

RailsClub 2017

Slide 14

Slide 14 text

THE TALK

Slide 15

Slide 15 text

LINES OF DEFENSE or what else authorization is not palkan_tula palkan 15 * lost in translation

Slide 16

Slide 16 text

palkan_tula palkan 16 authentication authorization constraints validations LINES OF DEFENSE

Slide 17

Slide 17 text

palkan_tula palkan AUTHORIZATION Is system allows to do that? 17 SYSTEM CONSTRAINTS ПО ПОМЫТОМУ НЕ ХОДИТЬ! * No step on washed floor

Slide 18

Slide 18 text

palkan_tula palkan BY EXAMPLE 18 class ReposController < AppController def create if current_account.available_repos.zero? head :payment_required end # ... end end constraint check

Slide 19

Slide 19 text

palkan_tula palkan AUTHORIZATION 19 Authorization model How to grant/revoke access? (roles, permission, accesses) Authorization layer How to verify access? (policies, rules, helpers)

Slide 20

Slide 20 text

palkan_tula palkan AUTHORIZATION 20 Model Layer

Slide 21

Slide 21 text

FORMAL MODELS palkan_tula palkan 21 Authorization Models DAC MAC RBAC

Slide 22

Slide 22 text

palkan_tula palkan ?AC 22 ? User Resource Access Control

Slide 23

Slide 23 text

palkan_tula palkan DAC 23 Discretionary Permission 1 0,* 0,* 1 User Resource

Slide 24

Slide 24 text

palkan_tula palkan DAC 24 Discretionary # => granting permissions @stove.permissions.create!(user: @vovka, activity: :cook) # => checking permissions @stove.permissions.exists?(user: @vovka, activity: :cook)

Slide 25

Slide 25 text

palkan_tula palkan DAC 25 Discretionary Permission 0,* 0,* UserGroup Resource 1 1

Slide 26

Slide 26 text

palkan_tula palkan MAC 26 User Resource Mandatory Top Secret Confidential Unclassified

Slide 27

Slide 27 text

palkan_tula palkan MAC 27 Mandatory # => checking permissions @report.security_level <= @colleague.security_clearance # => MUST have security level >= user clearance @colleague.reports.create!(params)

Slide 28

Slide 28 text

palkan_tula palkan MAC 28 Bell & LaPadula (Multi-level security) Chinese Wall http://www.cs.cornell.edu/courses/cs5430/2015sp/notes/mac.php

Slide 29

Slide 29 text

palkan_tula palkan RBAC 29 Role-based Role 0, * 0,* User Resource PrivilegeA PrivilegeB

Slide 30

Slide 30 text

palkan_tula palkan RBAC https://github.com/the-teacher/the_role 30

Slide 31

Slide 31 text

palkan_tula palkan ABAC 31 Attribute-based Policy User Resource attributes attributes Context

Slide 32

Slide 32 text

palkan_tula palkan 32 ABAC Attribute-based class Ability def initialize(user) can :sleep, Bed, user_id: user.id end end class BedPolicy def sleep? bed.user_id == user.id end end

Slide 33

Slide 33 text

palkan_tula palkan 33 Allow if time between 9 and 5 09:00:00 17:00:00 XACML eXtensible Access Control Markup Language

Slide 34

Slide 34 text

palkan_tula palkan 34 namespace exampleBoolean { policy article{ target clause userRole == “editor" and actionId == "edit" and itemType == "article" apply firstApplicable rule publishedArticles { target clause published == true permit } } } ALFA (XACML) Abbreviated Language For Authorization

Slide 35

Slide 35 text

palkan_tula palkan 35 [ { "resource": “TaleApp ::Bed", "action": ["sleep"], "description": "Allow owners to sleep in their beds”, "effect": "allow", "conditions": [ { "equal": { "resource ::owner ::id": ["user ::id"] } } ] } ] https://github.com/TheClimateCorporation/iron_hide ABAC Attribute-based

Slide 36

Slide 36 text

WHICH MODEL TO CHOOSE?

Slide 37

Slide 37 text

palkan_tula palkan IN REAL LIFE 37 RBAC MAC DAC ABAC

Slide 38

Slide 38 text

palkan_tula palkan class CoursePolicy < ApplicationPolicy def show? user.permissions.view_courses? && (record.account_id == user.account_id) && ( record.owner_id == user.id || record.visibility_level <= user.visibility || user.assigned_courses.where(id: record.id).exists? ) end end IN REAL LIFE 38

Slide 39

Slide 39 text

palkan_tula palkan class CoursePolicy < ApplicationPolicy def show? user.permissions.view_courses? && (record.account_id == user.account_id) && ( record.owner_id == user.id || record.visibility_level <= user.visibility || user.assigned_courses.where(id: record.id).exists? ) end end IN REAL LIFE 39 RBAC

Slide 40

Slide 40 text

palkan_tula palkan class CoursePolicy < ApplicationPolicy def show? user.permissions.view_courses? && (record.account_id == user.account_id) && ( record.owner_id == user.id || record.visibility_level <= user.visibility || user.assigned_courses.where(id: record.id).exists? ) end end IN REAL LIFE 40 ABAC

Slide 41

Slide 41 text

palkan_tula palkan class CoursePolicy < ApplicationPolicy def show? user.permissions.view_courses? && (record.account_id == user.account_id) && ( record.owner_id == user.id || record.visibility_level <= user.visibility || user.assigned_courses.where(id: record.id).exists? ) end end IN REAL LIFE 41 MAC

Slide 42

Slide 42 text

palkan_tula palkan class CoursePolicy < ApplicationPolicy def show? user.permissions.view_courses? && (record.account_id == user.account_id) && ( record.owner_id == user.id || record.visibility_level <= user.visibility || user.assigned_courses.where(id: record.id).exists? ) end end IN REAL LIFE 42 DAC

Slide 43

Slide 43 text

palkan_tula palkan AUTHORIZATION MODEL 43 Could not be generalized well Should reflects your business domain rules => No “right tool for the job”

Slide 44

Slide 44 text

palkan_tula palkan AUTHORIZATION 44 Authorization model How to grant/revoke access? (roles, permission, accesses) Authorization layer How to verify access? (policies, rules, helpers) ✅

Slide 45

Slide 45 text

palkan_tula palkan AUTHORIZATION LAYER 45

Slide 46

Slide 46 text

palkan_tula palkan AUTHORIZATION LAYER 46 Controllers (Rails), Actions (Hanami) Views (templates, serializers) Channels (Action Cable) GraphQL mutations and types

Slide 47

Slide 47 text

palkan_tula palkan LAYER: PROBLEMS 47 performance ? backend -> frontend maintainability ? ?

Slide 48

Slide 48 text

SOLUTIONS and their problems palkan_tula palkan 48

Slide 49

Slide 49 text

AUTHORIZATION eaco SimonSays action_policy the_role cancancan pundit kan moat declarative_authorization access-granted six consul

Slide 50

Slide 50 text

palkan_tula palkan CANCAN(-CAN) 50 class Ability include CanCan ::Ability def user_abilities can :create, [Question, Answer] can :update, [Question, Answer], user_id: user.id can :destroy, [Question, Answer], user_id: user.id can :destroy, Attachment, attachable: { user_id: user.id } can [:vote_up, :vote_down], [Question, Answer] do |resource| resource.user_id != user.id end end end

Slide 51

Slide 51 text

palkan_tula palkan 51 Ability

Slide 52

Slide 52 text

palkan_tula palkan PUNDIT 52 class QuestionPolicy def index? true end def create? true end def update? user.admin? || (user.id == target.user_id) end end

Slide 53

Slide 53 text

palkan_tula palkan PUNDIT 53 ls -r app/policies class RolePolicy < ApplicationPolicy end

Slide 54

Slide 54 text

palkan_tula palkan THE EVOLUTION 54 Start with CanCan Migrate to Pundit Customize Pundit Customize Pundit…

Slide 55

Slide 55 text

palkan_tula palkan THE EVOLUTION 55 Start with CanCan Migrate to Pundit Write your own framework

Slide 56

Slide 56 text

palkan_tula palkan THE EVOLUTION 56 Start with CanCan Migrate to Pundit Write your own framework — Kan

Slide 57

Slide 57 text

palkan_tula palkan THE EVOLUTION 57 Start with CanCan Migrate to Pundit Write your own framework — Action Policy

Slide 58

Slide 58 text

gem "action_policy"

Slide 59

Slide 59 text

palkan_tula palkan RAILSCONF 2018 http://bit.ly/action-policy-rc2018 59

Slide 60

Slide 60 text

palkan_tula palkan class ProductsController < ApplicationController before_action :load_product, except: [:index, :new, :create] def load_product @product = current_account.products.find(params[:id]) # auto-infer policy and rule authorize! @product # explicit rule and policy authorize! @product, to: :manage?, with: SpecialProductPolicy end end ACTION POLICY 60

Slide 61

Slide 61 text

palkan_tula palkan class ProductPolicy < ApplicationPolicy relation_scope do |rel| next rel if user.manager? rel.where(owner_id: user.id) end def create? user.manager? end def show? record.account_id == user.account_id end end ACTION POLICY 61

Slide 62

Slide 62 text

palkan_tula palkan IT’S PUNDIT, ISN’T IT? 62

Slide 63

Slide 63 text

palkan_tula palkan Action Policy has flexible architecture 63

Slide 64

Slide 64 text

palkan_tula palkan SEATTLERB 2019 http://bit.ly/action-policy-2019 64

Slide 65

Slide 65 text

palkan_tula palkan LAYER: PROBLEMS 65 performance ? backend -> frontend maintainability ? ?

Slide 66

Slide 66 text

palkan_tula palkan 66 performance ? LAYER: PROBLEMS

Slide 67

Slide 67 text

palkan_tula palkan PERFORMANCE 67 “Heavy” rules N+1 authorization

Slide 68

Slide 68 text

palkan_tula palkan “HEAVY” RULES 68 Complex SQL queries External API calls (e.g., external authorization service)

Slide 69

Slide 69 text

HOW TO FIND BOTTLENECKS?

Slide 70

Slide 70 text

palkan_tula palkan INSTRUMENTATION 70 Instrument policy checks (both raising and non-raising) Instrument authorize! calls separately => Detect missing checks, monitor high “Access denied” rate, etc.

Slide 71

Slide 71 text

palkan_tula palkan N+1 AUTHORIZATION 71 14 Mar 2018 14:08:30.722241 <190>1 2018-03-14T11:08:30.349156+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] Started GET "/test_account/openings/public-funnel/dashboard" for 185.13.112.107 at 2018-03-14 11:08:30 +0000 14 Mar 2018 14:08:30.722186 <190>1 2018-03-14T11:08:30.358367+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] Processing by DashboardsController#show as HTML 14 Mar 2018 14:08:30.722216 <190>1 2018-03-14T11:08:30.358391+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] Parameters: {"account_slug"=>"test_account", "opening_id"=>"public-funnel"} 14 Mar 2018 14:08:30.722174 <190>1 2018-03-14T11:08:30.368142+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.scope.hit=0ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.399348+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.scope.miss=6ms 14 Mar 2018 14:08:30.722176 <190>1 2018-03-14T11:08:30.407044+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.scope.miss=21ms 14 Mar 2018 14:08:30.722174 <190>1 2018-03-14T11:08:30.441693+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.scope.hit=0ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.471756+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=2ms 14 Mar 2018 14:08:30.722174 <190>1 2018-03-14T11:08:30.474080+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.504566+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.505893+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.507222+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.508522+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.509846+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.722175 <190>1 2018-03-14T11:08:30.511189+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.796175 <190>1 2018-03-14T11:08:30.512857+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.miss=1ms 14 Mar 2018 14:08:30.796174 <190>1 2018-03-14T11:08:30.523828+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:30.796174 <190>1 2018-03-14T11:08:30.527719+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:30.796174 <190>1 2018-03-14T11:08:30.529970+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:30.928734+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.039388+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.040101+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.137807+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.138677+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.139952+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.140577+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.141658+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.142342+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.143228+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.143837+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.144657+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.145339+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.146206+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.146852+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.147671+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.148303+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.149119+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.149849+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.150702+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.151359+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.152254+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.152879+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.153761+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.295174 <190>1 2018-03-14T11:08:31.154353+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.370174 <190>1 2018-03-14T11:08:31.155193+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.370174 <190>1 2018-03-14T11:08:31.155826+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.370174 <190>1 2018-03-14T11:08:31.156625+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 14 Mar 2018 14:08:31.370174 <190>1 2018-03-14T11:08:31.157286+00:00 app web.1 - - [1bb8d912-308a-4966-b436-a407ef34bac2] [U#6] measure#action_policy.check.hit=0ms 45 policy checks per request

Slide 72

Slide 72 text

palkan_tula palkan 72 N+1 AUTHORIZATION <% @course.materials.each do |material| %>
  • <%= material.title %> <% if allowed_to?(:edit?, @course) %> = link_to "Edit", material <% end %>
  • <% end %>

    Slide 73

    Slide 73 text

    palkan_tula palkan BETTER PERFORMANCE 73 Use cache Use cache Use cache

    Slide 74

    Slide 74 text

    palkan_tula palkan 74 ACTION POLICY: CACHE LAYERS

    Slide 75

    Slide 75 text

    palkan_tula palkan CACHE 75

    Slide 76

    Slide 76 text

    palkan_tula palkan PERFORMANCE 76 How to measure? — Instrumentation How to fix? — Cache

    Slide 77

    Slide 77 text

    palkan_tula palkan 77 maintainability ? LAYER: PROBLEMS

    Slide 78

    Slide 78 text

    palkan_tula palkan MAINTAINABILITY 78 Boilerplate Testability Debuggability

    Slide 79

    Slide 79 text

    palkan_tula palkan MAINTAINABILITY 79 Boilerplate Testability Debuggability See previous talks

    Slide 80

    Slide 80 text

    TESTABILITY or effective authorization testing palkan_tula palkan 80 *slap s**t together and deploy

    Slide 81

    Slide 81 text

    100% COVERAGE

    Slide 82

    Slide 82 text

    100% BUSINESS LOGIC COVERAGE

    Slide 83

    Slide 83 text

    HOW TO TEST

    Slide 84

    Slide 84 text

    palkan_tula palkan THE SURVEY 84 % of answers 0 17.5 35 52.5 70 requests/controllers tests unit tests (e.g. policy tests) system tests other Where do you test authorization logic?

    Slide 85

    Slide 85 text

    palkan_tula palkan IN REAL LIFE 85 describe "GET #index" do subject { get :index, params: {account_id: account.id} } include_examples "account access" include_examples "permission access", "view_reports" end Testing in controllers

    Slide 86

    Slide 86 text

    palkan_tula palkan 86 55% 45% Authorization Other Testing in controllers IN REAL LIFE ⛔

    Slide 87

    Slide 87 text

    palkan_tula palkan AUTHORIZATION TESTS 87 Test that the required authorization has been performed — just one test!

    Slide 88

    Slide 88 text

    palkan_tula palkan 88 describe "GET #index" do subject { get :index, params: {account_slug: account.slug} } it "is authorized" do policy = instance_double(ApplicantPolicy, index ?: false) expect(ApplicantPolicy).to receive(:new) .with(user, Applicant) { policy } expect { subject } .to raise_error Pundit ::NotAuthorizedError end end PUNDIT

    Slide 89

    Slide 89 text

    palkan_tula palkan 89 describe "GET #index" do subject { get :index, params: {account_slug: account.slug} } it "is authorized" do expect { subject } .to be_authorized_to(:index?).with(ApplicantPolicy) end end ACTION POLICY

    Slide 90

    Slide 90 text

    palkan_tula palkan AUTHORIZATION TESTS 90 Test that the required authorization has been performed — just one test! Test that the required scoping has been applied

    Slide 91

    Slide 91 text

    palkan_tula palkan ACTION POLICY 91 @posts = authorized_scope(Post.all) expect { subject }.to have_authorized_scope(:relation) .with_policy(PostPolicy) .with_target { |target| expect(target).to eq(Post.all) }

    Slide 92

    Slide 92 text

    palkan_tula palkan AUTHORIZATION TESTS 92 Test that the required authorization has been performed — just one test! Test that the required scoping has been applied Test authorization rules (policy classes) separately

    Slide 93

    Slide 93 text

    binding.pry

    Slide 94

    Slide 94 text

    DEBUGGABILITY or why does it fail? palkan_tula palkan 94

    Slide 95

    Slide 95 text

    palkan_tula palkan 95 def rsvp? rsvp_opened? && show? && (seats_available? || rsvp_to_pack?) end EXAMPLE Why can this check fail?

    Slide 96

    Slide 96 text

    palkan_tula palkan 96 def rsvp? binding.irb rsvp_opened? && show? && (seats_available? || rsvp_to_pack?) end EXAMPLE Why can this check fail? MAYBE BREAK?

    Slide 97

    Slide 97 text

    palkan_tula palkan 97 EXAMPLE Typical debugging session

    Slide 98

    Slide 98 text

    palkan_tula palkan 98 EXAMPLE Debugging with Action Policy

    Slide 99

    Slide 99 text

    palkan_tula palkan DEBUGGABILITY 99 Debugging PORO is much easier than debugging DSL Provide debugging helpers => make developers happier

    Slide 100

    Slide 100 text

    palkan_tula palkan 100 backend -> frontend ? LAYER: PROBLEMS

    Slide 101

    Slide 101 text

    palkan_tula palkan BACKEND VS FRONTEND 101 How to share permissions? How to “raise” actionable errors?

    Slide 102

    Slide 102 text

    REIMPLEMENT RULES IN JAVASCRIPT!

    Slide 103

    Slide 103 text

    NO TALK CAN AVOID GRAPHQL

    Slide 104

    Slide 104 text

    palkan_tula palkan GRAPHQL CASE 104 class EventPolicy < ApplicationPolicy def rsvp? # checks end end One rule to render the button

    Slide 105

    Slide 105 text

    palkan_tula palkan GRAPHQL CASE 105 class EventPolicy < ApplicationPolicy def rsvp? check?(:no_rsvp_manager?) && check?(:rsvp_opened?) && show? && check?(:seats_available?) && allowed_to?(:rsvp_to_pack?) end end different reasons => different messages

    Slide 106

    Slide 106 text

    FAILURE REASONS or “Not authorized” is not enough palkan_tula palkan 106

    Slide 107

    Slide 107 text

    palkan_tula palkan REASONS 107 Provide information on why action is not allowed Allow generate actionable/meaningful errors

    Slide 108

    Slide 108 text

    palkan_tula palkan 108 class ApplicantPolicy < ApplicationPolicy def show? allowed_to?(:view?) && allowed_to?(:show?, stage) end def view? user.has_permission?(:view_applicants) end end Wrap nested checks to track their results ACTION POLICY: REASONS

    Slide 109

    Slide 109 text

    palkan_tula palkan 109 # in controller rescue_from ActionPolicy ::Unauthorized do |ex| # either p exception.reasons.details # => { stage: [:show?] } # or p exception.reasons.details # => { applicant: [:view?] } end ACTION POLICY: REASONS

    Slide 110

    Slide 110 text

    palkan_tula palkan PERMISSIONS API 110

    Slide 111

    Slide 111 text

    palkan_tula palkan GRAPHQL CASE 111

    Slide 112

    Slide 112 text

    gem "action_policy-graphql"

    Slide 113

    Slide 113 text

    palkan_tula palkan 113 https://evl.ms/blog/exposing-permissions-in-graphql-apis-with-action-policy ACTION_POLICY-GRAPHQL

    Slide 114

    Slide 114 text

    palkan_tula palkan 114 Never expose authorization model Expose activity permissions (canDoSmth) via API BACKEND VS FRONTEND

    Slide 115

    Slide 115 text

    palkan_tula palkan 115 true/false is not enough Provide human-readable feedback Provide machine-readable details BACKEND VS FRONTEND

    Slide 116

    Slide 116 text

    AUTHORIZATION IS… palkan_tula palkan 116

    Slide 117

    Slide 117 text

    palkan_tula palkan AUTHORIZATION IS… 117 …as simple as adding ability.rb

    Slide 118

    Slide 118 text

    palkan_tula palkan AUTHORIZATION IS… 118 …a critical part of your application requiring special attention …a model you need to choose or design yourself …a mechanism to enforce the policies (authorization layer)

    Slide 119

    Slide 119 text

    palkan_tula palkan AUTHORIZATION 119 Authorization model — Use your brain Authorization layer — Use Action Policy

    Slide 120

    Slide 120 text

    СПАСИБО! Vladimir Dementyev @evilmartians evilmartians.com @palkan @palkan_tula