class Funcify::Afn

Constants

PRIVILEGE
RESOURCE

Public Class Methods

action_test() click to toggle source
# File lib/funcify/afn.rb, line 259
def action_test
  -> req, token { token_match.(req).(Fn.at.(4).(token)) }.curry
end
activity_match() click to toggle source
# File lib/funcify/afn.rb, line 202
def activity_match
  -> tests, r, a, activities {
    Fn.find.(policy_match.(tests, r, a)).(activities)
  }.curry
end
activity_policy() click to toggle source

The Activity-based access control policy


@param activities [String] A collection of activity strings assigned to the user obtained from Identity. For example:

=> ["lic:account:resource:billing_entity:*","lic:account:resource:payment_method:*"]

@param filter_fn A fn used to filter the activities. Afn provides a for_system fn which removes

any activities not associated with the system under test.  Fn.identity could be used to retain
all activities, or you can use any other fn that takes the activities as the last param

@param ctx {} service/resource/action being tested; e.g. {:resource=>:invoice, :action=>:create} Policy runs 4 tests: + the ctx includes a :resource key + the ctx includes an :action key + the ctx includes an :activities + and finally, the significant test, the service/resource/action match an activity

# File lib/funcify/afn.rb, line 123
def activity_policy
  -> activities, filter_fn, ctx {
    Fn.either.( Fn.tests.(Fn.all?, activity_tests), Fn.success, Fn.failure ).(ctx.merge(activities: filter_fn.(activities)))
  }.curry
end
activity_tests() click to toggle source
# File lib/funcify/afn.rb, line 129
def activity_tests
  [
    key_present_test.(:resource),
    key_present_test.(:action),
    key_present_test.(:activities),
    has_activity.(resource_activity_policy_tests)
  ]
end
activity_token_matcher() click to toggle source
# File lib/funcify/afn.rb, line 217
def activity_token_matcher
  -> tests, r, a, tokens {
    Fn.tests.(Fn.all?, tests.(r, a)).(tokens)
  }.curry
end
all_authorisor() click to toggle source

alias of authorise, which works with fmapping; hence all policy checks MUST be Success

# File lib/funcify/afn.rb, line 37
def all_authorisor
  -> enforcer, policies, ctx {
    Fn.compose.(  finally_fn.(enforcer),
                  Fn.fmap_compose.(policies)
      ).(Success(ctx))
  }.curry
end
any_finally_fn() click to toggle source

any results Success(), then Success, otherwise call enforcer

# File lib/funcify/afn.rb, line 59
def any_finally_fn
  -> enforcer, results {
    Fn.either.(Fn.any?.(Fn.maybe_value_ok?), Fn.success).(-> x { enforcer.(x) }).(results)
 }.curry
end
authorise() click to toggle source

Authorise Fns

@param enforcer A fn that responds to call and is invoked when the authorisations fail @param policy_predicates An array of Policy Predicates. The policy takes optional state and ctx and returns a Maybe. Available checkers are:

+ Afn.activity_policy
+ Afn.privilege_policy
+ Afn.slack_token_policy

@param ctx A data structure of the user's authz context understood by the policy checker; e.g. the activity being performed Example > Afn.authorise.(Afn.auth_error_raise_enforcer.(PolicyEnforcement::AuthorisationFailed))

.([Afn.activity_policy.(system_policy)])
.(activity_ctx(:create))

> Success(nil) | raises a PolicyEnforcement::AuthorisationFailed

Use partial evaluation by establishing the enforcer and policies (which returns a partially applied authorise fn) passing this to the services to be protected, which that evaluate the fn by providing the ctx.

# File lib/funcify/afn.rb, line 28
def authorise
  -> enforcer, policies, ctx {
    Fn.compose.(  finally_fn.(enforcer),
                  Fn.fmap_compose.(policies)
      ).(Success(ctx))
  }.curry
end
error_raiser() click to toggle source
# File lib/funcify/afn.rb, line 71
def error_raiser
  -> exception, ctx { raise exception unless ctx.success? }.curry
end
finally_fn() click to toggle source
# File lib/funcify/afn.rb, line 54
def finally_fn
  -> enforcer, v { Fn.either.(Fn.maybe_value_ok?).(Fn.identity).(-> x { enforcer.(x) } ).(v) }.curry
end
for_system() click to toggle source

Helper Fns

# File lib/funcify/afn.rb, line 171
def for_system
  -> system, activities {
    Fn.remove.(-> a { !a.include?(system.to_s)}).(activities)
  }.curry
end
has_activity() click to toggle source

s: system r: r a: action activitys: user's activity enum

# File lib/funcify/afn.rb, line 189
def has_activity
  -> tests, ctx  {
    activity_match.(tests, ctx[:resource], ctx[:action]).(ctx[:activities])
  }.curry
end
has_priviled() click to toggle source
# File lib/funcify/afn.rb, line 251
def has_priviled
  -> req, token { Fn.at.(2,token) == PRIVILEGE }.curry
end
has_privileged_access() click to toggle source
# File lib/funcify/afn.rb, line 195
def has_privileged_access
  -> tests, ctx  {
    activity_match.(tests, ctx[:privilege], ctx[:action]).(ctx[:activities])
  }.curry
end
has_resource() click to toggle source
# File lib/funcify/afn.rb, line 247
def has_resource
  -> req, token { Fn.at.(2,token) == RESOURCE }.curry
end
key_present_test() click to toggle source
# File lib/funcify/afn.rb, line 177
def key_present_test
  -> k, ctx { ctx.has_key? k }.curry
end
nil_enforcer() click to toggle source

Enforcement Fns

# File lib/funcify/afn.rb, line 67
def nil_enforcer
  Fn.identity
end
or_authorisor() click to toggle source

Similar to all_authorisor, except at least one of the policies MUST be success

# File lib/funcify/afn.rb, line 46
def or_authorisor
  -> enforcer, policies, ctx {
    Fn.compose.(  any_finally_fn.(enforcer),
                  Fn.map.(-> policy { policy.(ctx) } )
      ).(policies)
  }.curry
end
policy_match() click to toggle source
# File lib/funcify/afn.rb, line 208
def policy_match
  -> tests, r, a, activity {
    Fn.compose.(  activity_token_matcher.(tests, r,a),
                  Fn.coherse.(:to_sym),
                  Fn.split.(":")
      ).(activity)
  }.curry
end
privilege_activity_policy_tests() click to toggle source
# File lib/funcify/afn.rb, line 223
def privilege_activity_policy_tests
  -> r, a {
    [
      has_priviled.(r),
      resource_test.(r),
      action_test.(a)
    ]
  }
end
privilege_policy() click to toggle source

The Privelged access control policy


@param activities [String] A collection of activity strings assigned to the user obtained from Identity. For example:

=> ["lic:account:privilege:billing_entity:*","lic:account:privilege:payment_method:*"]

@param filter_fn A fn used to filter the activities. Afn provides a for_system fn which removes

any activities not associated with the system under test.  Fn.identity could be used to retain
all activities, or you can use any other fn that takes the activities as the last param

@param ctx {} service/resource/action being tested for privileged access; e.g. {:resource=>:invoice, :action=>:create} Policy runs 4 tests: + the ctx includes a :privilege key + the ctx includes an :action key + the ctx includes an :activities + and finally, the significant test, the service/resource/action match an activity

# File lib/funcify/afn.rb, line 151
def privilege_policy
  -> activities, filter_fn, ctx {
    Fn.either.(Fn.tests.(Fn.all?, privilege_tests), Fn.success, Fn.failure ).(ctx.merge(activities: filter_fn.(activities)))
  }.curry
end
privilege_tests() click to toggle source
# File lib/funcify/afn.rb, line 157
def privilege_tests
  [
    key_present_test.(:privilege),
    key_present_test.(:action),
    key_present_test.(:activities),
    has_privileged_access.(privilege_activity_policy_tests)
  ]
end
resource_activity_policy_tests() click to toggle source
# File lib/funcify/afn.rb, line 233
def resource_activity_policy_tests
  -> r, a {
    [
      has_resource.(r),
      resource_test.(r),
      action_test.(a)
    ]
  }
end
resource_test() click to toggle source
# File lib/funcify/afn.rb, line 255
def resource_test
  -> req, token { token_match.(req).(Fn.at.(3).(token)) }.curry
end
slack_token_policy() click to toggle source

Slack Policy


Slack Policy looks for :token in the ctx, and ensures that token is configured in Account. @param ctx {} expects a :token key/value provided by Slack.

# File lib/funcify/afn.rb, line 99
def slack_token_policy
  -> expected_token, ctx { Fn.either.(Fn.tests.(Fn.all?, slack_token_tests(expected_token)), Fn.success, Fn.failure ).(ctx) }.curry
end
slack_token_tests(token) click to toggle source
# File lib/funcify/afn.rb, line 103
def slack_token_tests(token)
  [
    key_present_test.(:token),
    valid_slack_token.(token)
  ]
end
system_test() click to toggle source
# File lib/funcify/afn.rb, line 243
def system_test
  -> req, token { token_match.(req).(Fn.at.(1).(token)) }.curry
end
token_match() click to toggle source
# File lib/funcify/afn.rb, line 263
def token_match
  -> req, token {
    # req.nil? || req.size == 0 || token == :* || token == req
    token == :* || token == req
  }.curry
end
valid_slack_token() click to toggle source
# File lib/funcify/afn.rb, line 181
def valid_slack_token
  -> t, ctx { ctx[:token] == t }.curry
end
validity_policy() click to toggle source

Valid JWT test


Takes any data structure and a function which validates it.

# File lib/funcify/afn.rb, line 89
def validity_policy
  -> policy_predicates, ctx {
    Fn.either.(Fn.tests.(Fn.all?, policy_predicates), Fn.success, Fn.failure ).(ctx)
  }.curry
end