conjur-policy-parser 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d09622031d68f8262a298eeec435416ad158d842
4
+ data.tar.gz: cadd61e2c1d4ba6f4d2737b44282cbd07c5ac3b4
5
+ SHA512:
6
+ metadata.gz: a58c0b1ad5c21aaf99b3501526d4496cbe410b4d6bc5dea3f353bd9808c3fbf5486360eef6541bff6e1c9c6ac0346b0a2281269210c555118e2905e7b966aaac
7
+ data.tar.gz: 5467b4b25b748ac3096184af3ee3c744de875e3e5ced0f31b5c7ac2b1e2df9e049acb5070c62a292848042e4c575633764736f1b02340c25e1f193f0f1cce290
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>conjur-policy-parser</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>com.aptana.ide.core.unifiedBuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>com.aptana.ruby.core.rubynature</nature>
16
+ <nature>com.aptana.projects.webnature</nature>
17
+ </natures>
18
+ </projectDescription>
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.4
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in conjur-policy-parser.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Kevin Gilpin
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,41 @@
1
+ # Conjur::Policy::Parser
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/conjur/policy/parser`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'conjur-policy-parser'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install conjur-policy-parser
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ 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.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/conjur-policy-parser.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'ci/reporter/rake/rspec'
4
+
5
+ RSpec::Core::RakeTask.new :spec
6
+
7
+ task :jenkins => ['ci:setup:rspec', :spec] do
8
+ end
9
+
10
+ task default: [:spec]
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "conjur/policy/parser"
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 "irb"
14
+ IRB.start
@@ -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,6 @@
1
+ #!/bin/bash -ex
2
+
3
+ cd /src/conjur-policy-parser
4
+ bundle
5
+
6
+ bundle exec rake jenkins || true
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'conjur-policy-parser-version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "conjur-policy-parser"
8
+ spec.version = Conjur::Policy::Parser::VERSION
9
+ spec.authors = ["Kevin Gilpin"]
10
+ spec.email = ["kgilpin@gmail.com"]
11
+
12
+ spec.summary = %q{Parse the Conjur policy YAML format.}
13
+ spec.homepage = "https://github.com/conjurinc/conjur-policy-parser"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "safe_yaml"
22
+ spec.add_dependency "activesupport", "~> 4.2"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.11"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "rspec-expectations"
28
+ spec.add_development_dependency "ci_reporter_rspec"
29
+ spec.add_development_dependency "simplecov"
30
+ spec.add_development_dependency "pry"
31
+ end
@@ -0,0 +1,27 @@
1
+ #!/bin/bash -ex
2
+
3
+ CONJUR_VERSION=${CONJUR_VERSION:-"4.8"}
4
+ DOCKER_IMAGE=${DOCKER_IMAGE:-"registry.tld/conjur-appliance-cuke-master:$CONJUR_VERSION-stable"}
5
+ NOKILL=${NOKILL:-"0"}
6
+ PULL=${PULL:-"1"}
7
+
8
+ if [ -z "$CONJUR_CONTAINER" ]; then
9
+ if [ "$PULL" == "1" ]; then
10
+ docker pull $DOCKER_IMAGE
11
+ fi
12
+
13
+ cid=$(docker run --privileged -d -v ${PWD}:/src/conjur-policy-parser $DOCKER_IMAGE)
14
+ function finish {
15
+ if [ "$NOKILL" != "1" ]; then
16
+ docker rm -f ${cid}
17
+ fi
18
+ }
19
+ trap finish EXIT
20
+
21
+ >&2 echo "Container id:"
22
+ >&2 echo $cid
23
+ else
24
+ cid=${CONJUR_CONTAINER}
25
+ fi
26
+
27
+ docker exec -i ${cid} /src/conjur-policy-parser/ci/test.sh
@@ -0,0 +1,7 @@
1
+ module Conjur
2
+ module Policy
3
+ module Parser
4
+ VERSION = "0.12.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ require 'conjur-policy-parser-version'
2
+ require 'yaml'
3
+ require 'safe_yaml'
4
+ require 'active_support'
5
+ require 'active_support/core_ext'
6
+ SafeYAML::OPTIONS[:default_mode] = :safe
7
+ SafeYAML::OPTIONS[:deserialize_symbols] = false
8
+
9
+ module Conjur
10
+ module Policy
11
+ end
12
+ end
13
+
14
+ require 'conjur/policy/logger'
15
+ require 'conjur/policy/invalid'
16
+ require 'conjur/policy/types/base'
17
+ require 'conjur/policy/types/include'
18
+ require 'conjur/policy/types/records'
19
+ require 'conjur/policy/types/member'
20
+ require 'conjur/policy/types/grant'
21
+ require 'conjur/policy/types/revoke'
22
+ require 'conjur/policy/types/permit'
23
+ require 'conjur/policy/types/deny'
24
+ require 'conjur/policy/types/create'
25
+ require 'conjur/policy/types/give'
26
+ require 'conjur/policy/types/retire'
27
+ require 'conjur/policy/types/update'
28
+ require 'conjur/policy/types/policy'
29
+ require 'conjur/policy/yaml/handler'
30
+ require 'conjur/policy/yaml/loader'
31
+ require 'conjur/policy/resolver'
32
+ require 'conjur/policy/doc'
@@ -0,0 +1,43 @@
1
+ module Conjur
2
+ module Policy
3
+ module Doc
4
+ Attribute = Struct.new(:id, :kind)
5
+
6
+ Operation = Struct.new(:id, :super_id, :description, :example, :attributes)
7
+
8
+ class << self
9
+ def list
10
+ all_types = Set.new
11
+ new_types = Set.new
12
+ new_types += Conjur::Policy::Types::Base.subclasses
13
+ all_types += new_types
14
+ while !new_types.empty?
15
+ iteration_new_types = Set.new
16
+ new_types.each do |type|
17
+ subtypes = type.subclasses
18
+ iteration_new_types += (Set.new(subtypes) - all_types)
19
+ all_types += subtypes
20
+ end
21
+ new_types = iteration_new_types.dup
22
+ iteration_new_types.clear
23
+ end
24
+ all_types.map do |type|
25
+ # TODO: I am not sure what this is
26
+ next if type == Conjur::Policy::Ruby::Policy
27
+
28
+ description = type.send(:description) rescue ""
29
+ example = type.send(:example) rescue ""
30
+ attributes = type.fields.map do |id, kind|
31
+ Attribute.new(id, kind)
32
+ end
33
+ unless attributes.empty?
34
+ super_id = type.superclass.short_name rescue nil
35
+ super_id = nil if super_id == "Base"
36
+ Operation.new(type.short_name, super_id, description, example, attributes)
37
+ end
38
+ end.compact.sort{|a,b| a.id <=> b.id}
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ module Conjur
2
+ module Policy
3
+ class Invalid < Exception
4
+ attr_reader :mark
5
+
6
+ def initialize message, filename, mark
7
+ super [ "Error at line #{mark.line}, column #{mark.column} in #{filename}", message ].join(' : ')
8
+ @mark = mark
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Conjur::Policy::Logger
2
+ def self.included base
3
+ base.module_eval do
4
+ # Override the logger with this method.
5
+ cattr_accessor :logger
6
+
7
+ require 'logger'
8
+ self.logger = Logger.new(STDERR)
9
+ self.logger.level = (ENV['DEBUG'] == "true" ? Logger::DEBUG : Logger::INFO)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,262 @@
1
+ module Conjur
2
+ module Policy
3
+ class Resolver
4
+ attr_reader :account, :ownerid, :namespace
5
+
6
+ class << self
7
+ # Resolve records to the specified owner id and namespace.
8
+ def resolve records, account, ownerid, namespace = nil
9
+ resolver_classes = [ AccountResolver, IdSubstitutionResolver, AnnotationSubstitutionResolver, OwnerResolver, FlattenResolver, DuplicateResolver ]
10
+ resolver_classes.each do |cls|
11
+ resolver = cls.new account, ownerid, namespace
12
+ records = resolver.resolve records
13
+ end
14
+ records
15
+ end
16
+ end
17
+
18
+ # +account+ is required. It's the default account whenever no account is specified.
19
+ # +ownerid+ is required. Any records without an owner will be assigned this owner. The exception
20
+ # is records defined in a policy, which are always owned by the policy role unless an explicit owner
21
+ # is indicated (which would be rare).
22
+ # +namespace+ is optional. It's prepended to the id of every record, except for ids which begin
23
+ # with a '/' character.
24
+ def initialize account, ownerid, namespace = nil
25
+ @account = account
26
+ @ownerid = ownerid
27
+ @namespace = namespace
28
+
29
+ raise "account is required" unless account
30
+ raise "ownerid is required" unless ownerid
31
+ raise "ownerid must be fully qualified" unless ownerid.split(":", 3).length == 3
32
+ end
33
+
34
+ protected
35
+
36
+ # Traverse an Array-ish of records, calling a +handler+ method for each one.
37
+ # If a record is a Policy, then the +policy_handler+ is invoked, after the +handler+.
38
+ def traverse records, visited, handler, policy_handler = nil
39
+ Array(records).flatten.each do |record|
40
+ next unless visited.add?(id_of(record))
41
+
42
+ handler.call record, visited
43
+ policy_handler.call record, visited if policy_handler && record.is_a?(Types::Policy)
44
+ end
45
+ end
46
+
47
+ def id_of record
48
+ record.object_id
49
+ end
50
+ end
51
+
52
+ # Updates all nil +account+ fields to the default account.
53
+ class AccountResolver < Resolver
54
+ def resolve records
55
+ traverse records, Set.new, method(:resolve_account), method(:on_resolve_policy)
56
+ end
57
+
58
+ def resolve_account record, visited
59
+ if record.respond_to?(:account) && record.respond_to?(:account=) && record.account.nil?
60
+ record.account = @account
61
+ end
62
+ traverse record.referenced_records, visited, method(:resolve_account), method(:on_resolve_policy)
63
+ end
64
+
65
+ def on_resolve_policy policy, visited
66
+ traverse policy.body, visited, method(:resolve_account), method(:on_resolve_policy)
67
+ end
68
+ end
69
+
70
+ class SubstitutionResolver < Resolver
71
+ SUBSTITUTIONS = { "$namespace" => :namespace }
72
+
73
+ def resolve records
74
+ traverse records, Set.new, method(:resolve_field), method(:on_resolve_policy)
75
+ end
76
+
77
+ protected
78
+
79
+ def substitute! id
80
+ SUBSTITUTIONS.each do |k,v|
81
+ next unless value = send(v)
82
+ id.gsub! k, value
83
+ end
84
+ end
85
+
86
+ def on_resolve_policy policy, visited
87
+ saved_namespace = @namespace
88
+ @namespace = policy.id
89
+ traverse policy.body, visited, method(:resolve_field), method(:on_resolve_policy)
90
+ ensure
91
+ @namespace = saved_namespace
92
+ end
93
+ end
94
+
95
+ # Makes all ids absolute, by prepending the namespace (if any) and the enclosing policy (if any).
96
+ class IdSubstitutionResolver < SubstitutionResolver
97
+
98
+ def resolve_field record, visited
99
+ if record.respond_to?(:id) && record.respond_to?(:id=)
100
+ id = record.id
101
+ if id.blank?
102
+ raise "#{record.class.simple_name} has no id" unless namespace
103
+ id = namespace
104
+ elsif id[0] == '/'
105
+ id = id[1..-1]
106
+ else
107
+ if record.respond_to?(:resource_kind) && record.resource_kind == "user"
108
+ id = [ id, user_namespace ].compact.join('@')
109
+ else
110
+ id = [ namespace, id ].compact.join('/')
111
+ end
112
+ end
113
+
114
+ substitute! id
115
+
116
+ record.id = id
117
+ end
118
+
119
+ traverse record.referenced_records, visited, method(:resolve_field), method(:on_resolve_policy)
120
+ end
121
+
122
+ protected
123
+
124
+ def user_namespace
125
+ namespace.gsub('/', '-') if namespace
126
+ end
127
+ end
128
+
129
+ class AnnotationSubstitutionResolver < SubstitutionResolver
130
+ def resolve_field record, visited
131
+ if record.respond_to?(:annotations) && (annotations = record.annotations)
132
+ annotations.each do |k,v|
133
+ substitute! v
134
+ end
135
+ end
136
+
137
+ traverse record.referenced_records, visited, method(:resolve_field), method(:on_resolve_policy)
138
+ end
139
+ end
140
+
141
+ # Sets the owner field for any records which support it, and don't have an owner specified.
142
+ # Within a policy, the default owner is the policy role. For global records, the
143
+ # default owner is the +ownerid+ specified in the constructor.
144
+ class OwnerResolver < Resolver
145
+ def resolve records
146
+ traverse records, Set.new, method(:resolve_owner), method(:on_resolve_policy)
147
+ end
148
+
149
+ def resolve_owner record, visited
150
+ if record.respond_to?(:owner) && record.owner.nil?
151
+ record.owner = Types::Role.new(@ownerid)
152
+ end
153
+ end
154
+
155
+ def on_resolve_policy policy, visited
156
+ saved_ownerid = @ownerid
157
+ @ownerid = [ policy.account, "policy", policy.id ].join(":")
158
+ traverse policy.body, visited, method(:resolve_owner), method(:on_resolve_policy)
159
+ ensure
160
+ @ownerid = saved_ownerid
161
+ end
162
+ end
163
+
164
+ # Flattens and sorts all records into a single list, including YAML lists and policy body.
165
+ class FlattenResolver < Resolver
166
+ def resolve records
167
+ @result = []
168
+ traverse records, Set.new, method(:resolve_record), method(:on_resolve_policy)
169
+
170
+ # Sort record creation before anything else.
171
+ # Sort record creation in dependency order (if A owns B, then A will be created before B).
172
+ # Otherwise, preserve the existing order.
173
+
174
+ @stable_index = {}
175
+ @result.each_with_index do |obj, idx|
176
+ @stable_index[obj] = idx
177
+ end
178
+ @referenced_record_index = {}
179
+ @result.each_with_index do |obj, idx|
180
+ @referenced_record_index[obj] = obj.referenced_records.select{|r| r.respond_to?(:roleid)}.map(&:roleid)
181
+ end
182
+ @result.flatten.sort do |a,b|
183
+ score = sort_score(a) - sort_score(b)
184
+ if score == 0
185
+ if a.respond_to?(:roleid) && @referenced_record_index[b].member?(a.roleid) &&
186
+ b.respond_to?(:roleid) && @referenced_record_index[a].member?(b.roleid)
187
+ raise "Dependency cycle encountered between #{a} and #{b}"
188
+ elsif a.respond_to?(:roleid) && @referenced_record_index[b].member?(a.roleid)
189
+ score = -1
190
+ elsif b.respond_to?(:roleid) && @referenced_record_index[a].member?(b.roleid)
191
+ score = 1
192
+ else
193
+ score = @stable_index[a] - @stable_index[b]
194
+ end
195
+ end
196
+ score
197
+ end
198
+ end
199
+
200
+ protected
201
+
202
+ # Sort "Create" and "Record" objects to the front.
203
+ def sort_score record
204
+ if record.is_a?(Types::Create) || record.is_a?(Types::Record)
205
+ -1
206
+ else
207
+ 0
208
+ end
209
+ end
210
+
211
+ # Add the record to the result.
212
+ def resolve_record record, visited
213
+ @result += Array(record)
214
+ end
215
+
216
+ # Recurse on the policy body records.
217
+ def on_resolve_policy policy, visited
218
+ body = policy.body
219
+ policy.remove_instance_variable "@body"
220
+ traverse body, visited, method(:resolve_record), method(:on_resolve_policy)
221
+ end
222
+ end
223
+
224
+ # Raises an exception if the same record is declared more than once.
225
+ class DuplicateResolver < Resolver
226
+ def resolve records
227
+ seen = Set.new
228
+ Array(records).flatten.each do |record|
229
+ if record.respond_to?(:id) && !seen.add?([ record.class.short_name, record.id ])
230
+ raise "#{record} is declared more than once"
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ # Unsets attributes that make for more verbose YAML output. This class is used to
237
+ # compact YAML expectations in test cases. It expects pre-flattened input.
238
+ #
239
+ # +account+ attributes which match the provided account are set to nil.
240
+ # +owner+ attributes which match the provided ownerid are removed.
241
+ class CompactOutputResolver < Resolver
242
+ def resolve records
243
+ traverse records, Set.new, method(:resolve_owner)
244
+ traverse records, Set.new, method(:resolve_account)
245
+ end
246
+
247
+ def resolve_account record, visited
248
+ if record.respond_to?(:account) && record.respond_to?(:account=) && record.account && record.account == self.account
249
+ record.remove_instance_variable :@account
250
+ end
251
+ traverse record.referenced_records, visited, method(:resolve_account)
252
+ end
253
+
254
+ def resolve_owner record, visited
255
+ if record.respond_to?(:owner) && record.respond_to?(:owner=) && record.owner && record.owner.roleid == self.ownerid
256
+ record.remove_instance_variable :@owner
257
+ end
258
+ traverse record.referenced_records, visited, method(:resolve_owner)
259
+ end
260
+ end
261
+ end
262
+ end