Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[RubyRussia 2019] Welcome, or access denied

[RubyRussia 2019] Welcome, or access denied

Video (RU): https://www.youtube.com/watch?v=y15a2g7v8i0

Popular Ruby frameworks bring us a lot of useful tools out-of-the-box, but there are missing parts too.
For example, for such essential task as authorization we are on our own.
The variety of open source solutions comes with the problem of choice—there is no silver bullet.

Nevertheless, it's possible to extract common patterns of designing authorization systems and define common technical problems, such as:
performance, code maintainability and testability, integration with client-side applications.

This talk aims to shed light on both theoretical and practical problems: from different authorization models to useful code techniques I came up with while working
on the Action Policy framework (https://actionpolicy.evilmartians.io) .

Vladimir Dementyev

September 28, 2019
Tweet

More Decks by Vladimir Dementyev

Other Decks in Programming

Transcript

  1. WELCOME
    Vladimir
    Dementyev
    or access denied?

    View Slide

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

    View Slide

  3. AUTHORIZATION
    or naming is hard
    palkan_tula
    palkan
    3

    View Slide

  4. I’M USING DEVISE
    FOR AUTHORIZATION!

    View Slide

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

    View Slide

  6. palkan_tula
    palkan
    Am I allowed
    to do that?
    6
    AUTHORIZATION

    View Slide

  7. HELLO

    View Slide

  8. palkan_tula
    palkan
    VLADIMIR
    DEMENTYEV
    8
    " Brooklyn # Moscow $

    View Slide

  9. palkan_tula
    palkan
    9

    View Slide

  10. View Slide

  11. palkan_tula
    palkan
    evl.ms/blog
    11

    View Slide

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

    View Slide

  13. RailsClub 2017

    View Slide

  14. THE TALK

    View Slide

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

    View Slide

  16. palkan_tula
    palkan
    16
    authentication
    authorization
    constraints
    validations
    LINES OF
    DEFENSE

    View Slide

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

    View Slide

  18. 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

    View Slide

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

    View Slide

  20. palkan_tula
    palkan
    AUTHORIZATION
    20
    Model Layer

    View Slide

  21. FORMAL
    MODELS
    palkan_tula
    palkan
    21
    Authorization Models
    DAC
    MAC
    RBAC

    View Slide

  22. palkan_tula
    palkan
    ?AC
    22
    ?
    User Resource
    Access Control

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  26. palkan_tula
    palkan
    MAC
    26
    User Resource
    Mandatory
    Top Secret
    Confidential
    Unclassified

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. 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

    View Slide

  33. palkan_tula
    palkan
    33

    Allow if time between 9 and 5




    09:00:00
    AttributeId="urn:oasis:names:tc:xacml:1.0:environment:current-time" MustBePresent="false" DataType="http: // www.w3.org/2001/XMLSchema#time" />






    17:00:00
    AttributeId="urn:oasis:names:tc:xacml:1.0:environment:current-time" MustBePresent="false" DataType="http: // www.w3.org/2001/XMLSchema#time" />





    XACML
    eXtensible Access Control Markup Language

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

  36. WHICH MODEL
    TO CHOOSE?

    View Slide

  37. palkan_tula
    palkan
    IN REAL LIFE
    37
    RBAC
    MAC
    DAC
    ABAC

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

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

    View Slide

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


    View Slide

  45. palkan_tula
    palkan
    AUTHORIZATION LAYER
    45

    View Slide

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

    View Slide

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

    View Slide

  48. SOLUTIONS
    and their problems
    palkan_tula
    palkan
    48

    View Slide

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

    View Slide

  50. 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

    View Slide

  51. palkan_tula
    palkan
    51
    Ability

    View Slide

  52. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  58. gem "action_policy"

    View Slide

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

    View Slide

  60. 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

    View Slide

  61. 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

    View Slide

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

    View Slide

  63. palkan_tula
    palkan
    Action Policy has flexible architecture
    63

    View Slide

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

    View Slide

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

    View Slide

  66. palkan_tula
    palkan
    66
    performance
    ?
    LAYER: PROBLEMS

    View Slide

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

    View Slide

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

    View Slide

  69. HOW TO FIND
    BOTTLENECKS?

    View Slide

  70. 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.

    View Slide

  71. 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

    View Slide

  72. 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 %>

    View Slide

  73. palkan_tula
    palkan
    BETTER PERFORMANCE
    73
    Use cache
    Use cache
    Use cache

    View Slide

  74. palkan_tula
    palkan
    74
    ACTION POLICY: CACHE LAYERS

    View Slide

  75. palkan_tula
    palkan
    CACHE
    75

    View Slide

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

    View Slide

  77. palkan_tula
    palkan
    77
    maintainability
    ?
    LAYER: PROBLEMS

    View Slide

  78. palkan_tula
    palkan
    MAINTAINABILITY
    78
    Boilerplate
    Testability
    Debuggability

    View Slide

  79. palkan_tula
    palkan
    MAINTAINABILITY
    79
    Boilerplate
    Testability
    Debuggability
    See previous talks

    View Slide

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

    View Slide

  81. 100%
    COVERAGE

    View Slide

  82. 100%
    BUSINESS LOGIC
    COVERAGE

    View Slide

  83. HOW TO TEST

    View Slide

  84. 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?

    View Slide

  85. 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

    View Slide

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

    View Slide

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

    View Slide

  88. 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

    View Slide

  89. 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

    View Slide

  90. 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

    View Slide

  91. 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)
    }

    View Slide

  92. 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

    View Slide

  93. binding.pry

    View Slide

  94. DEBUGGABILITY
    or why does it fail?
    palkan_tula
    palkan
    94

    View Slide

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

    View Slide

  96. 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?

    View Slide

  97. palkan_tula
    palkan
    97
    EXAMPLE
    Typical debugging session

    View Slide

  98. palkan_tula
    palkan
    98
    EXAMPLE
    Debugging with Action Policy

    View Slide

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

    View Slide

  100. palkan_tula
    palkan
    100
    backend -> frontend
    ?
    LAYER: PROBLEMS

    View Slide

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

    View Slide

  102. REIMPLEMENT
    RULES IN JAVASCRIPT!

    View Slide

  103. NO TALK CAN
    AVOID GRAPHQL

    View Slide

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

    View Slide

  105. 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

    View Slide

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

    View Slide

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

    View Slide

  108. 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

    View Slide

  109. 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

    View Slide

  110. palkan_tula
    palkan
    PERMISSIONS API
    110

    View Slide

  111. palkan_tula
    palkan
    GRAPHQL CASE
    111

    View Slide

  112. gem "action_policy-graphql"

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  116. AUTHORIZATION
    IS…
    palkan_tula
    palkan
    116

    View Slide

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

    View Slide

  118. 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)

    View Slide

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

    View Slide

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

    View Slide