funcify 0.4.25

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '0420107038bad57cdfd421e108fe0ac3f1f0c9ecddceb3c676dc717ac11e44f3'
4
+ data.tar.gz: cd7ab73950ec24788764d1df712404a6a3427e835118ca8c4b3e8a2f6760f046
5
+ SHA512:
6
+ metadata.gz: bde0dcf1bb42b99556dc906bb52a39a0de80eec7022c603a5cf0a5e145244992f01b5e6dcbb3cb4a49ff3111ac95b467faed0d8d840ec01877df815043212d29
7
+ data.tar.gz: 3e909ed15b8ee35ad3eb55e7486c6f3643cb890bb90aa62f9255a853e0504c9c9b9f2da0f096b1eea0c7f2b3a6e7c98b8c63b7312cfdacd89f57768dc43cfc62
Binary file
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .ds_store
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.4.1
7
+ before_install: gem install bundler -v 1.16.6
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at wild.fauve@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in funcify.gemspec
6
+ gemspec
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ funcify (0.4.24)
5
+ dry-monads (~> 1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.2)
11
+ concurrent-ruby (1.1.6)
12
+ diff-lcs (1.3)
13
+ dry-core (0.4.9)
14
+ concurrent-ruby (~> 1.0)
15
+ dry-equalizer (0.3.0)
16
+ dry-monads (1.3.5)
17
+ concurrent-ruby (~> 1.0)
18
+ dry-core (~> 0.4, >= 0.4.4)
19
+ dry-equalizer
20
+ method_source (0.9.2)
21
+ pry (0.12.2)
22
+ coderay (~> 1.1.0)
23
+ method_source (~> 0.9.0)
24
+ rake (13.0.1)
25
+ rspec (3.8.0)
26
+ rspec-core (~> 3.8.0)
27
+ rspec-expectations (~> 3.8.0)
28
+ rspec-mocks (~> 3.8.0)
29
+ rspec-core (3.8.0)
30
+ rspec-support (~> 3.8.0)
31
+ rspec-expectations (3.8.2)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.8.0)
34
+ rspec-mocks (3.8.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.8.0)
37
+ rspec-support (3.8.0)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bundler (~> 2.1.4)
44
+ funcify!
45
+ pry
46
+ rake (~> 13.0)
47
+ rspec (~> 3.0)
48
+
49
+ BUNDLED WITH
50
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Col Perks
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,83 @@
1
+ # Funcify
2
+
3
+ Fn is a collection of Higher Level functions implemented with simple lambdas and currying. And why not. Its not meant to be complete, not very sophisticated. Rather is a "play with functions" project. Its split into 2 parts:
4
+
5
+ + Common higher-level functions, such as `compose` or `flat_map`
6
+ + More specialised authorisation testing functions, designed for adding authorisation tests to your code.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'funcify'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install funcify
23
+
24
+ ## Usage
25
+
26
+ ### General Higher Level Functions
27
+
28
+ ### Authorisation Functions
29
+
30
+ The `#authorise` function takes the following arguments:
31
+ + enforcer. A function that will be given the result (wrapped in a Monad) of the authorisation policies and can decide what to do with it. There are 2 defined;
32
+ - `nil_enforcer`, which is an identity function and return the input value
33
+ - `auth_error_raise_enforcer`, which takes 2 curried arguments; an exception to raise and the value
34
+ + policies. Each policy is a function that takes the context and returns either a Success Monad or a Failure Monad wrapping the context. Thats it.
35
+ + data context. The data context is the input into the authorisation policies. Its provided as the last argument so that the remainder (basically the configuration) can be partially applied, and then the partially applied function can be passed around and given state when you want.
36
+
37
+ The simplist policy (and perhaps the most useless) would look like this:
38
+
39
+ ```ruby
40
+ -> ctx { M.Success(ctx) }
41
+ ```
42
+
43
+ Takes in the context and returns it wrapped in a success. Nice!
44
+
45
+ Lets have a look at the supplied Slack Token policy. The policy looks like this;
46
+
47
+ ```ruby
48
+ -> expected_token, ctx { Fn.either.(Fn.tests.(Fn.all?, slack_token_tests(expected_token)), Fn.success, Fn.failure ).(ctx) }.curry
49
+ ```
50
+ What this is doing is, taking the expected Slack token, a context that (somewhere) will contain a provided Slack token, running all the tests defined by `slack_token_tests`, and if that all pass, return the context wrapped in a Success Monad. The significant test is
51
+
52
+ ```ruby
53
+ -> t, ctx { ctx[:token] == t }.curry
54
+ ```
55
+
56
+ where `t` is the expected token, and the context (`ctx`) is a hash containing a key `token` which must equal the expected token.
57
+
58
+ So, to set this up, to be partially applied, we can start by defining the policy execution functions.
59
+
60
+ ```ruby
61
+ policy_fn = Afn.authorise.(Afn.auth_error_raise_enforcer.(StandardError)).([Fn::Afn.slack_token_policy.("slack_token")])
62
+
63
+ # then at sometime later, perhaps when we have the token, we can then check it by applying the last argument, the ctx
64
+ result = policy_fn.({token: "slack_token"}) # => Success({token: "slack_token"})
65
+ ```
66
+
67
+ ## Development
68
+
69
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
70
+
71
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
72
+
73
+ ## Contributing
74
+
75
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/fn. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
76
+
77
+ ## License
78
+
79
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
80
+
81
+ ## Code of Conduct
82
+
83
+ Everyone interacting in the Fn project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/fn/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "funcify"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "pry"
14
+ Pry.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,44 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "funcify/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "funcify"
8
+ spec.version = Funcify::VERSION
9
+ spec.authors = ["Col Perks"]
10
+ spec.email = ["wild.fauve@gmail.com"]
11
+
12
+ spec.summary = %q{Useful Higher Order Ruby Functions.}
13
+ spec.homepage = "https://github.com/wildfauve/funcify"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
20
+ #
21
+ # spec.metadata["homepage_uri"] = spec.homepage
22
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
23
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
24
+ else
25
+ raise "RubyGems 2.0 or newer is required to protect against " \
26
+ "public gem pushes."
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_dependency 'dry-monads', '~>1.0'
39
+
40
+ spec.add_development_dependency "bundler", "~> 2.1.4"
41
+ spec.add_development_dependency "rake", "~> 13.0"
42
+ spec.add_development_dependency "rspec", "~> 3.0"
43
+ spec.add_development_dependency "pry"
44
+ end
Binary file
@@ -0,0 +1,15 @@
1
+ require "funcify/version"
2
+ require 'dry/monads/result'
3
+ require 'dry/monads/try'
4
+
5
+
6
+ module Funcify
7
+ autoload :Fn, 'funcify/fn'
8
+ autoload :Afn, 'funcify/afn'
9
+ autoload :Map, 'funcify/map'
10
+ autoload :Record, 'funcify/record'
11
+ autoload :Monad, 'funcify/monad'
12
+ autoload :Cond, 'funcify/cond'
13
+ autoload :Str, 'funcify/str'
14
+ autoload :FSet, 'funcify/fset'
15
+ end
@@ -0,0 +1,274 @@
1
+ module Funcify
2
+
3
+ class Afn
4
+
5
+ extend Dry::Monads::Result::Mixin
6
+
7
+ PRIVILEGE = :privilege
8
+ RESOURCE = :resource
9
+
10
+ class << self
11
+
12
+ #
13
+ # Authorise Fns
14
+ #
15
+ # @param enforcer A fn that responds to #call and is invoked when the authorisations fail
16
+ # @param policy_predicates An array of Policy Predicates. The policy takes optional state and ctx and returns a Maybe. Available checkers are:
17
+ # + Afn.activity_policy
18
+ # + Afn.privilege_policy
19
+ # + Afn.slack_token_policy
20
+ # @param ctx A data structure of the user's authz context understood by the policy checker; e.g. the activity being performed
21
+ # Example
22
+ # > Afn.authorise.(Afn.auth_error_raise_enforcer.(PolicyEnforcement::AuthorisationFailed))
23
+ # .([Afn.activity_policy.(system_policy)])
24
+ # .(activity_ctx(:create))
25
+ # => Success(nil) | raises a PolicyEnforcement::AuthorisationFailed
26
+ # Use partial evaluation by establishing the enforcer and policies (which returns a partially applied authorise fn)
27
+ # passing this to the services to be protected, which that evaluate the fn by providing the ctx.
28
+ def authorise
29
+ -> enforcer, policies, ctx {
30
+ Fn.compose.( finally_fn.(enforcer),
31
+ Fn.fmap_compose.(policies)
32
+ ).(Success(ctx))
33
+ }.curry
34
+ end
35
+
36
+ # alias of #authorise, which works with fmapping; hence all policy checks MUST be Success
37
+ def all_authorisor
38
+ -> enforcer, policies, ctx {
39
+ Fn.compose.( finally_fn.(enforcer),
40
+ Fn.fmap_compose.(policies)
41
+ ).(Success(ctx))
42
+ }.curry
43
+ end
44
+
45
+ # Similar to all_authorisor, except at least one of the policies MUST be success
46
+ def or_authorisor
47
+ -> enforcer, policies, ctx {
48
+ Fn.compose.( any_finally_fn.(enforcer),
49
+ Fn.map.(-> policy { policy.(ctx) } )
50
+ ).(policies)
51
+ }.curry
52
+ end
53
+
54
+ def finally_fn
55
+ -> enforcer, v { Fn.either.(Fn.maybe_value_ok?).(Fn.identity).(-> x { enforcer.(x) } ).(v) }.curry
56
+ end
57
+
58
+ # any results Success(), then Success, otherwise call enforcer
59
+ def any_finally_fn
60
+ -> enforcer, results {
61
+ Fn.either.(Fn.any?.(Fn.maybe_value_ok?), Fn.success).(-> x { enforcer.(x) }).(results)
62
+ }.curry
63
+ end
64
+
65
+ #
66
+ # Enforcement Fns
67
+ def nil_enforcer
68
+ Fn.identity
69
+ end
70
+
71
+ def error_raiser
72
+ -> exception, ctx { raise exception unless ctx.success? }.curry
73
+ end
74
+
75
+ #
76
+ # Policy Predicate Fns
77
+ # ====================
78
+ #
79
+ # A Policy Predicate takes a context (any data structure it might or might not undestand), performs predicate
80
+ # tests using the context and the state, and returns an Either (Success or Failure)
81
+ # The ctx is specific to the test
82
+ #
83
+
84
+ #
85
+ # Valid JWT test
86
+ # --------------
87
+ #
88
+ # Takes any data structure and a function which validates it.
89
+ def validity_policy
90
+ -> policy_predicates, ctx {
91
+ Fn.either.(Fn.tests.(Fn.all?, policy_predicates), Fn.success, Fn.failure ).(ctx)
92
+ }.curry
93
+ end
94
+
95
+ # Slack Policy
96
+ # ------------
97
+ # Slack Policy looks for :token in the ctx, and ensures that token is configured in Account.
98
+ # @param ctx {} expects a :token key/value provided by Slack.
99
+ def slack_token_policy
100
+ -> expected_token, ctx { Fn.either.(Fn.tests.(Fn.all?, slack_token_tests(expected_token)), Fn.success, Fn.failure ).(ctx) }.curry
101
+ end
102
+
103
+ def slack_token_tests(token)
104
+ [
105
+ key_present_test.(:token),
106
+ valid_slack_token.(token)
107
+ ]
108
+ end
109
+
110
+ # The Activity-based access control policy
111
+ # ----------------------------------------
112
+ # @param activities [String] A collection of activity strings assigned to the user obtained from Identity. For example:
113
+ # => ["lic:account:resource:billing_entity:*","lic:account:resource:payment_method:*"]
114
+ # @param filter_fn A fn used to filter the activities. Afn provides a #for_system fn which removes
115
+ # any activities not associated with the system under test. Fn.identity could be used to retain
116
+ # all activities, or you can use any other fn that takes the activities as the last param
117
+ # @param ctx {} service/resource/action being tested; e.g. {:resource=>:invoice, :action=>:create}
118
+ # Policy runs 4 tests:
119
+ # + the ctx includes a :resource key
120
+ # + the ctx includes an :action key
121
+ # + the ctx includes an :activities
122
+ # + and finally, the significant test, the service/resource/action match an activity
123
+ def activity_policy
124
+ -> activities, filter_fn, ctx {
125
+ Fn.either.( Fn.tests.(Fn.all?, activity_tests), Fn.success, Fn.failure ).(ctx.merge(activities: filter_fn.(activities)))
126
+ }.curry
127
+ end
128
+
129
+ def activity_tests
130
+ [
131
+ key_present_test.(:resource),
132
+ key_present_test.(:action),
133
+ key_present_test.(:activities),
134
+ has_activity.(resource_activity_policy_tests)
135
+ ]
136
+ end
137
+
138
+ # The Privelged access control policy
139
+ # ----------------------------------------
140
+ # @param activities [String] A collection of activity strings assigned to the user obtained from Identity. For example:
141
+ # => ["lic:account:privilege:billing_entity:*","lic:account:privilege:payment_method:*"]
142
+ # @param filter_fn A fn used to filter the activities. Afn provides a #for_system fn which removes
143
+ # any activities not associated with the system under test. Fn.identity could be used to retain
144
+ # all activities, or you can use any other fn that takes the activities as the last param
145
+ # @param ctx {} service/resource/action being tested for privileged access; e.g. {:resource=>:invoice, :action=>:create}
146
+ # Policy runs 4 tests:
147
+ # + the ctx includes a :privilege key
148
+ # + the ctx includes an :action key
149
+ # + the ctx includes an :activities
150
+ # + and finally, the significant test, the service/resource/action match an activity
151
+ def privilege_policy
152
+ -> activities, filter_fn, ctx {
153
+ Fn.either.(Fn.tests.(Fn.all?, privilege_tests), Fn.success, Fn.failure ).(ctx.merge(activities: filter_fn.(activities)))
154
+ }.curry
155
+ end
156
+
157
+ def privilege_tests
158
+ [
159
+ key_present_test.(:privilege),
160
+ key_present_test.(:action),
161
+ key_present_test.(:activities),
162
+ has_privileged_access.(privilege_activity_policy_tests)
163
+ ]
164
+ end
165
+
166
+
167
+ #
168
+ # Helper Fns
169
+ #
170
+
171
+ def for_system
172
+ -> system, activities {
173
+ Fn.remove.(-> a { !a.include?(system.to_s)}).(activities)
174
+ }.curry
175
+ end
176
+
177
+ def key_present_test
178
+ -> k, ctx { ctx.has_key? k }.curry
179
+ end
180
+
181
+ def valid_slack_token
182
+ -> t, ctx { ctx[:token] == t }.curry
183
+ end
184
+
185
+ # s: system
186
+ # r: r
187
+ # a: action
188
+ # activitys: user's activity enum
189
+ def has_activity
190
+ -> tests, ctx {
191
+ activity_match.(tests, ctx[:resource], ctx[:action]).(ctx[:activities])
192
+ }.curry
193
+ end
194
+
195
+ def has_privileged_access
196
+ -> tests, ctx {
197
+ activity_match.(tests, ctx[:privilege], ctx[:action]).(ctx[:activities])
198
+ }.curry
199
+ end
200
+
201
+
202
+ def activity_match
203
+ -> tests, r, a, activities {
204
+ Fn.find.(policy_match.(tests, r, a)).(activities)
205
+ }.curry
206
+ end
207
+
208
+ def policy_match
209
+ -> tests, r, a, activity {
210
+ Fn.compose.( activity_token_matcher.(tests, r,a),
211
+ Fn.coherse.(:to_sym),
212
+ Fn.split.(":")
213
+ ).(activity)
214
+ }.curry
215
+ end
216
+
217
+ def activity_token_matcher
218
+ -> tests, r, a, tokens {
219
+ Fn.tests.(Fn.all?, tests.(r, a)).(tokens)
220
+ }.curry
221
+ end
222
+
223
+ def privilege_activity_policy_tests
224
+ -> r, a {
225
+ [
226
+ has_priviled.(r),
227
+ resource_test.(r),
228
+ action_test.(a)
229
+ ]
230
+ }
231
+ end
232
+
233
+ def resource_activity_policy_tests
234
+ -> r, a {
235
+ [
236
+ has_resource.(r),
237
+ resource_test.(r),
238
+ action_test.(a)
239
+ ]
240
+ }
241
+ end
242
+
243
+ def system_test
244
+ -> req, token { token_match.(req).(Fn.at.(1).(token)) }.curry
245
+ end
246
+
247
+ def has_resource
248
+ -> req, token { Fn.at.(2,token) == RESOURCE }.curry
249
+ end
250
+
251
+ def has_priviled
252
+ -> req, token { Fn.at.(2,token) == PRIVILEGE }.curry
253
+ end
254
+
255
+ def resource_test
256
+ -> req, token { token_match.(req).(Fn.at.(3).(token)) }.curry
257
+ end
258
+
259
+ def action_test
260
+ -> req, token { token_match.(req).(Fn.at.(4).(token)) }.curry
261
+ end
262
+
263
+ def token_match
264
+ -> req, token {
265
+ # req.nil? || req.size == 0 || token == :* || token == req
266
+ token == :* || token == req
267
+ }.curry
268
+ end
269
+
270
+ end
271
+
272
+ end
273
+
274
+ end