bitwarden-sdk-secrets 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a4a8efa2366ad5c6568ce9ec284857674c1b1730263bd538096a2861031ae833
4
+ data.tar.gz: 7e15ebee50e29c57357f958f1273fdda182035a490b555510cd95de0f49e14b0
5
+ SHA512:
6
+ metadata.gz: ac31e0a25f1ee16b2db1e2949cca718cd268a4ad0b2f18c28ffee48b1cf3ab5d3855bb771869037f00161255529f8d0715f628197e5309391162bca94e799c79
7
+ data.tar.gz: 52e926afe59c3d545d0d5441dc2cb356491291a7ffffa6af2ef7a61006dbf6ab3eef8d6662faf87eb1811d4c5eaf17a5a8756627252304868fb3b589ab0070ea
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ RuboCop::RakeTask.new
10
+
11
+ task default: :rubocop
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'bitwarden-sdk-secrets'
7
+ spec.version = BitwardenSDKSecrets::VERSION
8
+ spec.authors = ['Bitwarden Inc.']
9
+ spec.email = ['hello@bitwarden_sdk.com']
10
+
11
+ spec.summary = 'Bitwarden Secrets Manager SDK.'
12
+ spec.description = 'Ruby wrapper for Bitwarden secrets manager SDK.'
13
+ spec.homepage = 'https://bitwarden.com/products/secrets-manager/'
14
+ spec.required_ruby_version = '>= 3.0.0'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/bitwarden/sdk'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/bitwarden/sdk/blob/main/languages/ruby/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(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git Gemfile])
25
+ end
26
+ end
27
+
28
+ spec.files += Dir.glob('lib/linux-x64/**/*')
29
+ spec.files += Dir.glob('lib/macos-x64/**/*')
30
+ spec.files += Dir.glob('lib/windows-x64/**/*')
31
+ spec.files += Dir.glob('lib/macos-arm64/**/*')
32
+ spec.files += Dir.glob('lib/schemas.rb')
33
+
34
+ spec.bindir = 'exe'
35
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ['lib']
37
+
38
+ # Uncomment to register a new dependency of your gem
39
+ # spec.add_dependency "example-gem", "~> 1.0"
40
+ spec.add_dependency 'dry-struct', '~> 1.6'
41
+ spec.add_dependency 'dry-types', '~> 1.7'
42
+ spec.add_dependency 'ffi', '~> 1.15'
43
+ spec.add_dependency 'json', '~> 2.6'
44
+ spec.add_dependency 'rake', '~> 13.0'
45
+ spec.add_dependency 'rubocop', '~> 1.21'
46
+
47
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'dry-types'
5
+
6
+ require_relative 'schemas'
7
+ require_relative 'extended_schemas/schemas'
8
+ require_relative 'command_runner'
9
+ require_relative 'bitwarden_lib'
10
+ require_relative 'bitwarden_error'
11
+ require_relative 'projects'
12
+ require_relative 'secrets'
13
+
14
+ module BitwardenSDKSecrets
15
+ class BitwardenSettings
16
+ attr_accessor :api_url, :identity_url
17
+
18
+ def initialize(api_url, identity_url)
19
+ # if api_url.nil? || identity_url.nil?
20
+ # raise ArgumentError, "api_url and identity_url cannot be nil"
21
+ # end
22
+
23
+ @api_url = api_url
24
+ @identity_url = identity_url
25
+ end
26
+ end
27
+
28
+ class BitwardenClient
29
+ attr_reader :bitwarden, :project_client, :secrets_client
30
+
31
+ def initialize(bitwarden_settings)
32
+ client_settings = ClientSettings.new(
33
+ api_url: bitwarden_settings.api_url,
34
+ identity_url: bitwarden_settings.identity_url,
35
+ user_agent: 'Bitwarden RUBY-SDK',
36
+ device_type: nil
37
+ )
38
+
39
+ @bitwarden = BitwardenLib
40
+ @handle = @bitwarden.init(client_settings.to_dynamic.compact.to_json)
41
+ @command_runner = CommandRunner.new(@bitwarden, @handle)
42
+ @project_client = ProjectsClient.new(@command_runner)
43
+ @secrets_client = SecretsClient.new(@command_runner)
44
+ end
45
+
46
+ def access_token_login(access_token, state_file = nil)
47
+ access_token_request = AccessTokenLoginRequest.new(access_token: access_token, state_file: state_file)
48
+ @command_runner.run(SelectiveCommand.new(access_token_login: access_token_request))
49
+ nil
50
+ end
51
+
52
+ def free_mem
53
+ @bitwarden.free_mem(@handle)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BitwardenSDKSecrets
4
+ class BitwardenError < StandardError
5
+ def initialize(message = 'Error getting response')
6
+ super(message)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module BitwardenSDKSecrets
6
+ module BitwardenLib
7
+ extend FFI::Library
8
+
9
+ def self.mac_with_intel?
10
+ `uname -m`.strip == 'x86_64'
11
+ end
12
+
13
+ ffi_lib case RUBY_PLATFORM
14
+ when /darwin/
15
+ local_file = if mac_with_intel?
16
+ File.expand_path('macos-x64/libbitwarden_c.dylib', __dir__)
17
+ else
18
+ File.expand_path('macos-arm64/libbitwarden_c.dylib', __dir__)
19
+ end
20
+ File.exist?(local_file) ? local_file : File.expand_path('../../../../target/debug/libbitwarden_c.dylib', __dir__)
21
+ when /linux/
22
+ local_file = File.expand_path('linux-x64/libbitwarden_c.so', __dir__)
23
+ File.exist?(local_file) ? local_file : File.expand_path('../../../../target/debug/libbitwarden_c.so', __dir__)
24
+ when /mswin|mingw/
25
+ local_file = File.expand_path('windows-x64/bitwarden_c.dll', __dir__)
26
+ File.exist?(local_file) ? local_file : File.expand_path('../../../../target/debug/bitwarden_c.dll', __dir__)
27
+ else
28
+ raise "Unsupported platform: #{RUBY_PLATFORM}"
29
+ end
30
+
31
+ attach_function :init, [:string], :pointer
32
+ attach_function :run_command, %i[string pointer], :string
33
+ attach_function :free_mem, [:pointer], :void
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BitwardenSDKSecrets
4
+ class CommandRunner
5
+ def initialize(bitwarden_sdk, handle)
6
+ @bitwarden_sdk = bitwarden_sdk
7
+ @handle = handle
8
+ end
9
+
10
+ # @param [Dry-Struct] cmd
11
+ def run(cmd)
12
+ @bitwarden_sdk.run_command(cmd.to_json, @handle)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+
2
+ module BitwardenSDKSecrets
3
+ class SelectiveCommand < Command
4
+ attribute :password_login, PasswordLoginRequest.optional.default(nil)
5
+ attribute :api_key_login, APIKeyLoginRequest.optional.default(nil)
6
+ attribute :access_token_login, AccessTokenLoginRequest.optional.default(nil)
7
+ attribute :get_user_api_key, SecretVerificationRequest.optional.default(nil)
8
+ attribute :fingerprint, FingerprintRequest.optional.default(nil)
9
+ attribute :sync, SyncRequest.optional.default(nil)
10
+ attribute :secrets, SecretsCommand.optional.default(nil)
11
+ attribute :projects, ProjectsCommand.optional.default(nil)
12
+
13
+ def to_dynamic
14
+ {
15
+ "passwordLogin" => password_login&.to_dynamic,
16
+ "apiKeyLogin" => api_key_login&.to_dynamic,
17
+ "accessTokenLogin" => access_token_login&.to_dynamic,
18
+ "getUserApiKey" => get_user_api_key&.to_dynamic,
19
+ "fingerprint" => fingerprint&.to_dynamic,
20
+ "sync" => sync&.to_dynamic,
21
+ "secrets" => secrets&.to_dynamic,
22
+ "projects" => projects&.to_dynamic,
23
+ }.compact
24
+ end
25
+ end
26
+
27
+ class SelectiveProjectsCommand < ProjectsCommand
28
+ attribute :get, ProjectGetRequest.optional.default(nil)
29
+ attribute :create, ProjectCreateRequest.optional.default(nil)
30
+ attribute :list, ProjectsListRequest.optional.default(nil)
31
+ attribute :update, ProjectPutRequest.optional.default(nil)
32
+ attribute :delete, ProjectsDeleteRequest.optional.default(nil)
33
+
34
+ def to_dynamic
35
+ {
36
+ "get" => get&.to_dynamic,
37
+ "create" => create&.to_dynamic,
38
+ "list" => list&.to_dynamic,
39
+ "update" => update&.to_dynamic,
40
+ "delete" => delete&.to_dynamic,
41
+ }.compact
42
+ end
43
+ end
44
+
45
+ class SelectiveSecretsCommand < SecretsCommand
46
+ attribute :get, SecretGetRequest.optional.default(nil)
47
+ attribute :get_by_ids, SecretsGetRequest.optional.default(nil)
48
+ attribute :create, SecretCreateRequest.optional.default(nil)
49
+ attribute :list, SecretIdentifiersRequest.optional.default(nil)
50
+ attribute :update, SecretPutRequest.optional.default(nil)
51
+ attribute :delete, SecretsDeleteRequest.optional.default(nil)
52
+
53
+ def to_dynamic
54
+ {
55
+ "get" => get&.to_dynamic,
56
+ "getByIds" => get_by_ids&.to_dynamic,
57
+ "create" => create&.to_dynamic,
58
+ "list" => list&.to_dynamic,
59
+ "update" => update&.to_dynamic,
60
+ "delete" => delete&.to_dynamic,
61
+ }.compact
62
+ end
63
+ end
64
+ end
Binary file
Binary file
data/lib/projects.rb ADDED
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'bitwarden_error'
4
+
5
+ module BitwardenSDKSecrets
6
+ class ProjectsClient
7
+ def initialize(command_runner)
8
+ @command_runner = command_runner
9
+ end
10
+
11
+ def create_project(project_name, organization_id)
12
+ project_create_request = ProjectCreateRequest.new(
13
+ project_create_request_name: project_name,
14
+ organization_id: organization_id
15
+ )
16
+ command = create_command(
17
+ create: project_create_request
18
+ )
19
+ response = parse_response(command)
20
+
21
+ projects_response = ResponseForProjectResponse.from_json!(response).to_dynamic
22
+
23
+ if projects_response.key?('success') && projects_response['success'] == true &&
24
+ projects_response.key?('data')
25
+ return projects_response['data']
26
+ end
27
+
28
+ error_response(projects_response)
29
+ end
30
+
31
+ def get(project_id)
32
+ project_get_request = ProjectGetRequest.new(id: project_id)
33
+ command = create_command(get: project_get_request)
34
+ response = parse_response(command)
35
+
36
+ projects_response = ResponseForProjectResponse.from_json!(response).to_dynamic
37
+
38
+ if projects_response.key?('success') && projects_response['success'] == true &&
39
+ projects_response.key?('data')
40
+ return projects_response['data']
41
+ end
42
+
43
+ error_response(projects_response)
44
+ end
45
+
46
+ def list_projects(organization_id)
47
+ project_list_request = ProjectsListRequest.new(organization_id: organization_id)
48
+ command = create_command(list: project_list_request)
49
+ response = parse_response(command)
50
+
51
+ projects_response = ResponseForProjectsResponse.from_json!(response).to_dynamic
52
+
53
+ if projects_response.key?('success') && projects_response['success'] == true &&
54
+ projects_response.key?('data') && projects_response['data'].key?('data')
55
+ return projects_response['data']['data']
56
+ end
57
+
58
+ error_response(projects_response)
59
+ end
60
+
61
+ def update_project(id, project_put_request_name, organization_id)
62
+ project_put_request = ProjectPutRequest.new(
63
+ id: id,
64
+ project_put_request_name: project_put_request_name,
65
+ organization_id: organization_id
66
+ )
67
+ command = create_command(
68
+ update: project_put_request
69
+ )
70
+ response = parse_response(command)
71
+
72
+ projects_response = ResponseForProjectResponse.from_json!(response).to_dynamic
73
+
74
+ if projects_response.key?('success') && projects_response['success'] == true &&
75
+ projects_response.key?('data')
76
+ return projects_response['data']
77
+ end
78
+
79
+ error_response(projects_response)
80
+ end
81
+
82
+ def delete_projects(ids)
83
+ project_delete_request = ProjectsDeleteRequest.new(ids: ids)
84
+ command = create_command(delete: project_delete_request)
85
+ response = parse_response(command)
86
+
87
+ projects_response = ResponseForProjectsDeleteResponse.from_json!(response).to_dynamic
88
+
89
+ if projects_response.key?('success') && projects_response['success'] == true &&
90
+ projects_response.key?('data') && projects_response['data'].key?('data')
91
+ return projects_response['data']['data']
92
+ end
93
+
94
+ error_response(projects_response)
95
+ end
96
+
97
+ private
98
+
99
+ def error_response(response)
100
+ raise BitwardenError, response['errorMessage'] if response.key?('errorMessage')
101
+
102
+ raise BitwardenError, 'Error while getting response'
103
+ end
104
+
105
+ def create_command(commands)
106
+ SelectiveCommand.new(projects: SelectiveProjectsCommand.new(commands))
107
+ end
108
+
109
+ def parse_response(command)
110
+ response = @command_runner.run(command)
111
+ raise BitwardenError, 'Error getting response' if response.nil?
112
+
113
+ response
114
+ end
115
+ end
116
+ end