artifactory-permissions 0.1.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
+ SHA256:
3
+ metadata.gz: 1161328888803592d738756747044a3d022b4d6d590b06d65113edbfe1ecdc48
4
+ data.tar.gz: f01a33dc6d38fc78d49840d4927815bb597c55c80078ab8989bcc6dfc3ff7da7
5
+ SHA512:
6
+ metadata.gz: 3cdb700bf58cc589b79cb296eda1bed6547ebb4c6733a12289d4d753a1d304f971a4a4a0c757677074c691cc46f897bc1523aa1b739d70689dac1fa2d2c1487c
7
+ data.tar.gz: ec5ce848b5c6fd17e2e110cdb901d2250fa63a6f180b906b2ae144c24d6d560dba9301a88b20ea93c368f6568f1600f98c3ab62560803a520eaad2fa0cfcc156
@@ -0,0 +1,17 @@
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
+
13
+ .vscode
14
+ .idea
15
+ .byebug_history
16
+ Gemfile.lock
17
+ .env.local
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.6.5
6
+ before_install: gem install bundler -v 2.1.4
@@ -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 thomas.scholz@rubyapps.ch. 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 [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in artifactory-permissions.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
8
+ gem "webmock", "~> 3.8"
9
+ gem "byebug", "~> 11.1"
@@ -0,0 +1,118 @@
1
+ # Artifactory::Permissions
2
+
3
+ Some lines of code to edit users and groups in existing Artifactory permission targets
4
+ for users having "manage" permissions.
5
+
6
+ This is a WIP - by far not feature complete. ;)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'artifactory-permissions'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install artifactory-permissions
23
+
24
+ ## Usage
25
+
26
+
27
+ ### List permission targets accessible to your user
28
+ ```ruby
29
+ api_client = Artifactory::Permissions.api_client endpoint: 'http://artifactory.local',
30
+ api_key: 'your-api-key'
31
+
32
+
33
+ Artifactory::Permissions.list api_client: api_client
34
+ # => List of permission targets (json representation)
35
+ ```
36
+
37
+ ### Show / find / get permission target
38
+ ```ruby
39
+ api_client = Artifactory::Permissions.api_client endpoint: 'http://artifactory.local',
40
+ api_key: 'your-api-key'
41
+
42
+
43
+ _status, permission_target = Artifactory::Permissions.find name: "<pt-name>",
44
+ api_client: api_client
45
+ # => [:ok, PermissionTarget]
46
+
47
+ _status, permission_target = Artifactory::Permissions.get "<permission-target-resource-uri>",
48
+ api_client: api_client
49
+ # => [:ok, PermissionTarget]
50
+ ```
51
+
52
+ ### Add / update / delete users and groups
53
+ ```ruby
54
+ api_client = Artifactory::Permissions.api_client endpoint: 'http://artifactory.local',
55
+ api_key: 'your-api-key'
56
+
57
+ _status, permission_target = Artifactory::Permissions.find name: "<permission-target-name>",
58
+ api_client: api_client
59
+
60
+ user = Artifactory::Permissions.user name: "<user-name>",
61
+ permissions: %w[read write],
62
+ scope: "repo" # repo, build, releaseBundle
63
+
64
+ group = Artifactory::Permissions.group name: "<group-name>",
65
+ permissions: %w[read write],
66
+ scope: "repo" # repo, build, releaseBundle
67
+
68
+ # It does some validations on the user like presence of name, permissions etc.
69
+
70
+ _status, _permission_target, _errors =
71
+ Artifactory::Permissions.add_user permission_target: permission_target,
72
+ user: user
73
+
74
+ _status, _permission_target, _errors =
75
+ Artifactory::Permissions.add_group permission_target: permission_target,
76
+ group: group
77
+
78
+ other_user = Artifactory::Permissions.user name: "user-to-remove",
79
+ permissions: %w[],
80
+ scope: "repo" # repo, build, releaseBundle
81
+
82
+ other_group = Artifactory::Permissions.group name: "group-to-remove",
83
+ permissions: %w[],
84
+ scope: "repo" # repo, build, releaseBundle
85
+
86
+ _status, _permission_target, _errors =
87
+ Artifactory::Permissions.delete_user permission_target: permission_target,
88
+ user: user
89
+
90
+ _status, _permission_target, _errors =
91
+ Artifactory::Permissions.delete_group permission_target: permission_target,
92
+ group: group
93
+
94
+ # Finally submit it to the Artifactory
95
+
96
+ _status, _permission_target, _errors =
97
+ Artifactory::Permissions.save permission_target: permission_target,
98
+ api_client: api_client
99
+ ```
100
+
101
+ You may add multiple users and groups at a time. But keep in mind Artifactory rejects the
102
+ permission target on the first error it detects. Errors might be an user or group not yet present
103
+ to the Artifactory, unknown permissions, etc.
104
+
105
+ ## Development
106
+
107
+ 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.
108
+
109
+ 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).
110
+
111
+ ## Contributing
112
+
113
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/artifactory-permissions. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/artifactory-permissions/blob/master/CODE_OF_CONDUCT.md).
114
+
115
+
116
+ ## Code of Conduct
117
+
118
+ Everyone interacting in the Artifactory::Permissions project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/artifactory-permissions/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,30 @@
1
+ require_relative "lib/artifactory/permissions/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "artifactory-permissions"
5
+ spec.version = Artifactory::Permissions::VERSION
6
+ spec.authors = ["Thomas Scholz"]
7
+ spec.email = ["thomas.scholz@rubyapps.ch"]
8
+
9
+ spec.summary = %q{Ruby Lib for managing Artifactory PermissionTargets}
10
+ spec.description = %q{Ruby Lib for managing Artifactory PermissionTargets}
11
+ spec.homepage = "https://github.com/tscholz/artifactory-permissions"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "httparty", "~> 0.18"
30
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "artifactory/permissions"
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(__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,84 @@
1
+ require_relative "permissions/errors"
2
+ require_relative "permissions/helpers"
3
+ require_relative "permissions/v2"
4
+ require_relative "permissions/version"
5
+
6
+ module Artifactory
7
+ module Permissions
8
+ Error = Class.new StandardError
9
+ FatalError = Class.new Error
10
+
11
+ PERMISSIONS = %w[read write annotate delete manage managedXrayMeta distribute].freeze
12
+ SCOPES = %w[repo build releaseBundle].freeze
13
+
14
+ module_function
15
+
16
+ def list(api_client:)
17
+ api_client.list_permissions
18
+ end
19
+
20
+ def find(name:, api_client:)
21
+ status, result = api_client.find_permission name: name
22
+
23
+ status == :ok ? [:ok, V2.parse_permission_target(result)] : [status, result]
24
+ end
25
+
26
+ def get(uri, api_client:)
27
+ status, result = api_client.get uri
28
+
29
+ status == :ok ? [:ok, V2.parse_permission_target(result)] : [status, result]
30
+ end
31
+
32
+ def user(name:, permissions:, scope:)
33
+ V2::PermissionItems.user name: name,
34
+ permissions: permissions,
35
+ scope: scope
36
+ end
37
+
38
+ def group(name:, permissions:, scope:)
39
+ V2::PermissionItems.group name: name,
40
+ permissions: permissions,
41
+ scope: scope
42
+ end
43
+
44
+ def upsert_user(permission_target:, user:)
45
+ V2::Commands.upsert_item permission_target: permission_target,
46
+ item: user
47
+
48
+ [user.errors? ? :error : :ok, permission_target, user.errors]
49
+ end
50
+
51
+ def delete_user(permission_target:, user:)
52
+ V2::Commands.delete_item permission_target: permission_target,
53
+ item: user
54
+
55
+ [user.errors? ? :error : :ok, permission_target, user.errors]
56
+ end
57
+
58
+ def upsert_group(permission_target:, group:)
59
+ V2::Commands.upsert_item permission_target: permission_target,
60
+ item: group
61
+
62
+ [group.errors? ? :error : :ok, permission_target, group.errors]
63
+ end
64
+
65
+ def delete_group(permission_target:, group:)
66
+ V2::Commands.delete_item permission_target: permission_target,
67
+ item: group
68
+
69
+ [group.errors? ? :error : :ok, permission_target, group.errors]
70
+ end
71
+
72
+ def save(permission_target:, api_client:)
73
+ V2::Commands.save_permission_target permission_target: permission_target,
74
+ api_client: api_client
75
+
76
+ [permission_target.errors? ? :error : :ok, permission_target, permission_target.errors]
77
+ end
78
+
79
+ def api_client(endpoint:, api_key:)
80
+ V2.api_client endpoint: endpoint,
81
+ api_key: api_key
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ module Artifactory
2
+ module Permissions
3
+ class Errors
4
+ def initialize
5
+ @errors = Hash.new { |h, k| h[k] = [] }
6
+ end
7
+
8
+ def add(key, msg)
9
+ @errors[key] << msg
10
+ self
11
+ end
12
+
13
+ def empty?
14
+ !any?
15
+ end
16
+
17
+ alias_method :none?, :empty?
18
+
19
+ def any?
20
+ @errors.values.flatten.any?
21
+ end
22
+
23
+ def full_messages
24
+ @errors
25
+ .keys
26
+ .map { |key| [key, full_message(key)].join(": ") }
27
+ .join("; ")
28
+ end
29
+
30
+ def full_message(key)
31
+ @errors[key].join(", ")
32
+ end
33
+
34
+ def to_h
35
+ @errors.dup
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module Helpers
4
+ module_function
5
+
6
+ def deep_merge!(hash, other)
7
+ hash.merge!(other) do |key, val, other_val|
8
+ if val.is_a?(Hash) && other_val.is_a?(Hash)
9
+ deep_merge!(val, other_val)
10
+ else
11
+ other_val
12
+ end
13
+ end
14
+ end
15
+
16
+ def deep_hash
17
+ Hash.new { |h, k| h[k] = Hash.new &h.default_proc }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "v2/api_client"
2
+ require_relative "v2/commands"
3
+ require_relative "v2/permission_items"
4
+ require_relative "v2/permission_target"
5
+
6
+ module Artifactory
7
+ module Permissions
8
+ module V2
9
+ module_function
10
+
11
+ def api_client(endpoint:, api_key:)
12
+ ApiClient.new endpoint: endpoint,
13
+ api_key: api_key
14
+ end
15
+
16
+ def parse_permission_target(payload)
17
+ PermissionTarget.new payload
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,105 @@
1
+ require "cgi"
2
+ require "httparty"
3
+ require "json"
4
+
5
+ module Artifactory
6
+ module Permissions
7
+ module V2
8
+ class ApiClient
9
+ include HTTParty
10
+
11
+ HTTP_ERRORS = [
12
+ EOFError,
13
+ Errno::ECONNRESET,
14
+ Errno::EINVAL,
15
+ Net::HTTPBadResponse,
16
+ Net::HTTPHeaderSyntaxError,
17
+ Net::ProtocolError,
18
+ Timeout::Error,
19
+ ]
20
+
21
+ HANDLED_ERRORS = [SocketError] + HTTP_ERRORS
22
+
23
+ attr_reader :endpoint, :api_key
24
+
25
+ def initialize(endpoint:, api_key:)
26
+ @endpoint = endpoint
27
+ @api_key = api_key
28
+ end
29
+
30
+ def list_permissions
31
+ get "/api/v2/security/permissions"
32
+ end
33
+
34
+ def find_permission(name:)
35
+ get "/api/v2/security/permissions/#{url_safe name}"
36
+ end
37
+
38
+ def save_permission_target(permission_target)
39
+ put "/api/v2/security/permissions/#{url_safe permission_target.name}",
40
+ permission_target.payload
41
+ end
42
+
43
+ def get(path)
44
+ response = self.class.get uri_for(path), headers: headers
45
+
46
+ handle_response response
47
+ rescue *handled_errors => err
48
+ raise FatalError, err.message
49
+ end
50
+
51
+ def put(path, data)
52
+ response = self.class.put uri_for(path),
53
+ body: data.to_json,
54
+ headers: headers
55
+
56
+ handle_response response
57
+ rescue *handled_errors => err
58
+ raise FatalError, err.message
59
+ end
60
+
61
+ private
62
+
63
+ # Informational responses (100–199),
64
+ # Successful responses (200–299),
65
+ # Redirects (300–399),
66
+ # Client errors (400–499),
67
+ # and Server errors (500–599).
68
+ def handle_response(response)
69
+ response.success? ? [:ok, response.parsed_response] : [:error, handle_none_success!(response)]
70
+ end
71
+
72
+ def handle_none_success!(response)
73
+ case response
74
+ when :client_error?.to_proc
75
+ response.parsed_response.fetch "errors"
76
+ when :server_error?
77
+ raise FatalError, response.body
78
+ else
79
+ response.error!
80
+ end
81
+ end
82
+
83
+ def uri_for(path)
84
+ File.join endpoint, path
85
+ end
86
+
87
+ def headers
88
+ { "Content-Type" => "application/json" }.merge auth_headers
89
+ end
90
+
91
+ def auth_headers
92
+ { "X-JFrog-Art-Api" => api_key }
93
+ end
94
+
95
+ def url_safe(string)
96
+ CGI.escape string
97
+ end
98
+
99
+ def handled_errors
100
+ HANDLED_ERRORS
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "commands/permission_item_command"
2
+ require_relative "commands/delete_item"
3
+ require_relative "commands/save_permission"
4
+ require_relative "commands/upsert_item"
5
+
6
+ module Artifactory
7
+ module Permissions
8
+ module V2
9
+ module Commands
10
+ module_function
11
+
12
+ def upsert_item(permission_target:, item:)
13
+ UpsertItem.call permission_target: permission_target,
14
+ item: item
15
+ end
16
+
17
+ def delete_item(permission_target:, item:)
18
+ DeleteItem.call permission_target: permission_target,
19
+ item: item
20
+ end
21
+
22
+ def save_permission_target(permission_target:, api_client:)
23
+ SavePermission.call permission_target: permission_target,
24
+ api_client: api_client
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module V2
4
+ module Commands
5
+ class DeleteItem < PermissionItemCommand
6
+ private
7
+
8
+ def process
9
+ permission_target
10
+ .public_send("delete_#{item_type}", scope: scope, name: name)
11
+ end
12
+
13
+ def validate
14
+ errors.add :name,
15
+ "Name can not be empty." if name.empty?
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,66 @@
1
+ require "forwardable"
2
+
3
+ module Artifactory
4
+ module Permissions
5
+ module V2
6
+ module Commands
7
+ class PermissionItemCommand
8
+ extend Forwardable
9
+
10
+ def self.call(permission_target:, item:)
11
+ new(permission_target: permission_target, item: item).call
12
+ end
13
+
14
+ attr_reader :permission_target, :item
15
+
16
+ def initialize(permission_target:, item:)
17
+ @permission_target = permission_target
18
+ @item = item
19
+ end
20
+
21
+ def call
22
+ validate_scope
23
+
24
+ validate
25
+
26
+ process if valid?
27
+
28
+ self
29
+ end
30
+
31
+ private
32
+
33
+ def validate_scope
34
+ if scope.empty?
35
+ errors.add :scope,
36
+ "Scope can not be empty."
37
+ else
38
+ errors.add :scope,
39
+ "Unknown scope '#{scope}'. Must be one of #{available_scopes.join(", ")}." unless available_scopes.include? scope
40
+ end
41
+ end
42
+
43
+ def_delegators :item, :name, :permissions, :scope, :errors, :errors?, :valid?
44
+
45
+ def validate
46
+ # Subclass validations goes here
47
+ end
48
+
49
+ def process
50
+ NotImplementedError
51
+ end
52
+
53
+ def item_type
54
+ @item_type ||= item.class.name.split("::").last.downcase.tap do |type|
55
+ raise Error, "Unknown permission item type #{type}" unless %w[group user].include? type
56
+ end
57
+ end
58
+
59
+ def available_scopes
60
+ item.class.available_scopes
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ require "json"
2
+
3
+ module Artifactory
4
+ module Permissions
5
+ module V2
6
+ module Commands
7
+ class SavePermission
8
+ def self.call(permission_target:, api_client:)
9
+ new(permission_target: permission_target, api_client: api_client).call
10
+ end
11
+
12
+ attr_reader :permission_target, :api_client
13
+
14
+ def initialize(permission_target:, api_client:)
15
+ @permission_target = permission_target
16
+ @api_client = api_client
17
+ end
18
+
19
+ def call
20
+ process
21
+ permission_target
22
+ end
23
+
24
+ private
25
+
26
+ def process
27
+ status, result = api_client.save_permission_target permission_target
28
+
29
+ result.each { |err| add_error err } if status == :error
30
+ end
31
+
32
+ def add_error(err)
33
+ errors.add :base, [err["status"], err["message"]].join(" - ")
34
+ end
35
+
36
+ def errors
37
+ permission_target.errors
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module V2
4
+ module Commands
5
+ class UpsertItem < PermissionItemCommand
6
+ private
7
+
8
+ def process
9
+ permission_target
10
+ .public_send("upsert_#{item_type}", scope: scope, name: name, permissions: permissions)
11
+ end
12
+
13
+ def validate
14
+ errors.add :name,
15
+ "Name can not be empty." if name.empty?
16
+
17
+ errors.add :permissions,
18
+ "Permissions can not be empty." if permissions.empty?
19
+
20
+ validate_permission_items if permissions.any?
21
+ end
22
+
23
+ def validate_permission_items
24
+ (unknown_permissions = permissions - available_permissions).any? or return
25
+
26
+ errors.add :permissions,
27
+ "Permissions contain unknown value(s) #{unknown_permissions.join(", ")}. Valid permissions are #{available_permissions.join(", ")}."
28
+ end
29
+
30
+ def available_permissions
31
+ item.class.available_permissions
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "permission_items/base"
2
+ require_relative "permission_items/group"
3
+ require_relative "permission_items/user"
4
+
5
+ module Artifactory
6
+ module Permissions
7
+ module V2
8
+ module PermissionItems
9
+ module_function
10
+
11
+ def user(name:, permissions:, scope:)
12
+ User.new name: name, permissions: permissions, scope: scope
13
+ end
14
+
15
+ def group(name:, permissions:, scope:)
16
+ Group.new name: name, permissions: permissions, scope: scope
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module V2
4
+ module PermissionItems
5
+ class Base
6
+ def self.available_permissions
7
+ PERMISSIONS
8
+ end
9
+
10
+ def self.available_scopes
11
+ SCOPES
12
+ end
13
+
14
+ attr_reader :name, :permissions, :scope
15
+
16
+ def initialize(name:, permissions:, scope:)
17
+ @name = name.to_s
18
+ @permissions = Array(permissions).map(&:to_s)
19
+ @scope = scope.to_s
20
+ end
21
+
22
+ def valid?
23
+ !errors?
24
+ end
25
+
26
+ def errors?
27
+ errors.any?
28
+ end
29
+
30
+ def errors
31
+ @errors ||= Errors.new
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module V2
4
+ module PermissionItems
5
+ class Group < Base
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module V2
4
+ module PermissionItems
5
+ class User < Base
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,96 @@
1
+ module Artifactory
2
+ module Permissions
3
+ module V2
4
+ class PermissionTarget
5
+ attr_reader :payload
6
+
7
+ def initialize(payload)
8
+ @payload = Hash payload
9
+ end
10
+
11
+ def name
12
+ payload["name"]
13
+ end
14
+
15
+ def users(scope: nil)
16
+ find_all :user, scope
17
+ end
18
+
19
+ def groups(scope: nil)
20
+ find_all :group, scope
21
+ end
22
+
23
+ def upsert_user(scope:, name:, permissions:)
24
+ upsert! scope, "users", name, permissions
25
+ end
26
+
27
+ def delete_user(scope:, name:)
28
+ delete! scope, "users", name
29
+ end
30
+
31
+ def upsert_group(scope:, name:, permissions:)
32
+ upsert! scope, "groups", name, permissions
33
+ end
34
+
35
+ def delete_group(scope:, name:)
36
+ delete! scope, "groups", name
37
+ end
38
+
39
+ # def headers
40
+ # # Does not work.
41
+ # { "Content-Type" => "application/vnd.org.jfrog.artifactory.security.PermissionTargetV2+json" }
42
+ # end
43
+
44
+ def errors?
45
+ errors.any?
46
+ end
47
+
48
+ def errors
49
+ @errors ||= Errors.new
50
+ end
51
+
52
+ private
53
+
54
+ def find_all(type, scope)
55
+ scopes = scope ? Array(scope.to_s) : known_scopes
56
+
57
+ scopes.map do |current_scope|
58
+ payload
59
+ .dig(current_scope, "actions", "#{type}s")
60
+ .to_a
61
+ .map { |name, permissions|
62
+ PermissionItems.public_send type, name: name,
63
+ permissions: permissions,
64
+ scope: current_scope
65
+ }
66
+ end.flatten
67
+ end
68
+
69
+ def upsert!(scope, subject, name, permissions)
70
+ Helpers.deep_merge! payload,
71
+ deep_hash(scope, subject, name, permissions)
72
+
73
+ self
74
+ end
75
+
76
+ def delete!(scope, subject, name)
77
+ payload
78
+ .dig(scope, "actions", subject)
79
+ &.delete(name)
80
+
81
+ self
82
+ end
83
+
84
+ def deep_hash(scope, subject, name, permissions)
85
+ Helpers
86
+ .deep_hash
87
+ .tap { |h| h[scope]["actions"][subject][name] = permissions }
88
+ end
89
+
90
+ def known_scopes
91
+ Permissions::SCOPES
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,5 @@
1
+ module Artifactory
2
+ module Permissions
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: artifactory-permissions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Scholz
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ description: Ruby Lib for managing Artifactory PermissionTargets
28
+ email:
29
+ - thomas.scholz@rubyapps.ch
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - ".rspec"
36
+ - ".travis.yml"
37
+ - CODE_OF_CONDUCT.md
38
+ - Gemfile
39
+ - README.md
40
+ - Rakefile
41
+ - artifactory-permissions.gemspec
42
+ - bin/console
43
+ - bin/setup
44
+ - lib/artifactory/permissions.rb
45
+ - lib/artifactory/permissions/errors.rb
46
+ - lib/artifactory/permissions/helpers.rb
47
+ - lib/artifactory/permissions/v2.rb
48
+ - lib/artifactory/permissions/v2/api_client.rb
49
+ - lib/artifactory/permissions/v2/commands.rb
50
+ - lib/artifactory/permissions/v2/commands/delete_item.rb
51
+ - lib/artifactory/permissions/v2/commands/permission_item_command.rb
52
+ - lib/artifactory/permissions/v2/commands/save_permission.rb
53
+ - lib/artifactory/permissions/v2/commands/upsert_item.rb
54
+ - lib/artifactory/permissions/v2/permission_items.rb
55
+ - lib/artifactory/permissions/v2/permission_items/base.rb
56
+ - lib/artifactory/permissions/v2/permission_items/group.rb
57
+ - lib/artifactory/permissions/v2/permission_items/user.rb
58
+ - lib/artifactory/permissions/v2/permission_target.rb
59
+ - lib/artifactory/permissions/version.rb
60
+ homepage: https://github.com/tscholz/artifactory-permissions
61
+ licenses: []
62
+ metadata:
63
+ allowed_push_host: https://rubygems.org
64
+ homepage_uri: https://github.com/tscholz/artifactory-permissions
65
+ source_code_uri: https://github.com/tscholz/artifactory-permissions
66
+ changelog_uri: https://github.com/tscholz/artifactory-permissions/CHANGELOG.md
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.3.0
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.0.3
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Ruby Lib for managing Artifactory PermissionTargets
86
+ test_files: []