slosilo 0.0.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +0 -2
  2. data/LICENSE +2 -2
  3. data/README.md +8 -128
  4. data/lib/slosilo/adapters/abstract_adapter.rb +0 -4
  5. data/lib/slosilo/adapters/mock_adapter.rb +1 -14
  6. data/lib/slosilo/adapters/sequel_adapter/migration.rb +2 -5
  7. data/lib/slosilo/adapters/sequel_adapter.rb +5 -67
  8. data/lib/slosilo/attr_encrypted.rb +7 -33
  9. data/lib/slosilo/http_request.rb +59 -0
  10. data/lib/slosilo/key.rb +6 -129
  11. data/lib/slosilo/keystore.rb +12 -40
  12. data/lib/slosilo/rack/middleware.rb +123 -0
  13. data/lib/slosilo/symmetric.rb +17 -47
  14. data/lib/slosilo/version.rb +2 -21
  15. data/lib/slosilo.rb +2 -2
  16. data/lib/tasks/slosilo.rake +0 -10
  17. data/slosilo.gemspec +6 -19
  18. data/spec/http_request_spec.rb +107 -0
  19. data/spec/http_stack_spec.rb +44 -0
  20. data/spec/key_spec.rb +32 -175
  21. data/spec/keystore_spec.rb +2 -15
  22. data/spec/rack_middleware_spec.rb +109 -0
  23. data/spec/random_spec.rb +2 -12
  24. data/spec/sequel_adapter_spec.rb +22 -133
  25. data/spec/slosilo_spec.rb +12 -78
  26. data/spec/spec_helper.rb +15 -37
  27. data/spec/symmetric_spec.rb +26 -69
  28. metadata +51 -104
  29. checksums.yaml +0 -7
  30. data/.github/CODEOWNERS +0 -10
  31. data/.gitleaks.toml +0 -221
  32. data/.kateproject +0 -4
  33. data/CHANGELOG.md +0 -50
  34. data/CONTRIBUTING.md +0 -16
  35. data/Jenkinsfile +0 -132
  36. data/SECURITY.md +0 -42
  37. data/dev/Dockerfile.dev +0 -7
  38. data/dev/docker-compose.yml +0 -8
  39. data/lib/slosilo/adapters/file_adapter.rb +0 -42
  40. data/lib/slosilo/adapters/memory_adapter.rb +0 -31
  41. data/lib/slosilo/errors.rb +0 -15
  42. data/lib/slosilo/jwt.rb +0 -122
  43. data/publish.sh +0 -5
  44. data/secrets.yml +0 -1
  45. data/spec/encrypted_attributes_spec.rb +0 -114
  46. data/spec/file_adapter_spec.rb +0 -81
  47. data/spec/jwt_spec.rb +0 -102
  48. data/test.sh +0 -8
data/lib/slosilo/jwt.rb DELETED
@@ -1,122 +0,0 @@
1
- require 'json'
2
-
3
- module Slosilo
4
- # A JWT-formatted Slosilo token.
5
- # @note This is not intended to be a general-purpose JWT implementation.
6
- class JWT
7
- # Create a new unsigned token with the given claims.
8
- # @param claims [#to_h] claims to embed in this token.
9
- def initialize claims = {}
10
- @claims = JSONHash[claims]
11
- end
12
-
13
- # Parse a token in compact representation
14
- def self.parse_compact raw
15
- load *raw.split('.', 3).map(&Base64.method(:urlsafe_decode64))
16
- end
17
-
18
- # Parse a token in JSON representation.
19
- # @note only single signature is currently supported.
20
- def self.parse_json raw
21
- raw = JSON.load raw unless raw.respond_to? :to_h
22
- parts = raw.to_h.values_at(*%w(protected payload signature))
23
- fail ArgumentError, "input not a complete JWT" unless parts.all?
24
- load *parts.map(&Base64.method(:urlsafe_decode64))
25
- end
26
-
27
- # Add a signature.
28
- # @note currently only a single signature is handled;
29
- # the token will be frozen after this operation.
30
- def add_signature header, &sign
31
- @claims = canonicalize_claims.freeze
32
- @header = JSONHash[header].freeze
33
- @signature = sign[string_to_sign].freeze
34
- freeze
35
- end
36
-
37
- def string_to_sign
38
- [header, claims].map(&method(:encode)).join '.'
39
- end
40
-
41
- # Returns the JSON serialization of this JWT.
42
- def to_json *a
43
- {
44
- protected: encode(header),
45
- payload: encode(claims),
46
- signature: encode(signature)
47
- }.to_json *a
48
- end
49
-
50
- # Returns the compact serialization of this JWT.
51
- def to_s
52
- [header, claims, signature].map(&method(:encode)).join('.')
53
- end
54
-
55
- attr_accessor :claims, :header, :signature
56
-
57
- private
58
-
59
- # Create a JWT token object from existing header, payload, and signature strings.
60
- # @param header [#to_s] URLbase64-encoded representation of the protected header
61
- # @param payload [#to_s] URLbase64-encoded representation of the token payload
62
- # @param signature [#to_s] URLbase64-encoded representation of the signature
63
- def self.load header, payload, signature
64
- self.new(JSONHash.load payload).tap do |token|
65
- token.header = JSONHash.load header
66
- token.signature = signature.to_s.freeze
67
- token.freeze
68
- end
69
- end
70
-
71
- def canonicalize_claims
72
- claims[:iat] = Time.now unless claims.include? :iat
73
- claims[:iat] = claims[:iat].to_time.to_i
74
- claims[:exp] = claims[:exp].to_time.to_i if claims.include? :exp
75
- JSONHash[claims.to_a]
76
- end
77
-
78
- # Convenience method to make the above code clearer.
79
- # Converts to string and urlbase64-encodes.
80
- def encode s
81
- Base64.urlsafe_encode64 s.to_s
82
- end
83
-
84
- # a hash with a possibly frozen JSON stringification
85
- class JSONHash < Hash
86
- def to_s
87
- @repr || to_json
88
- end
89
-
90
- def freeze
91
- @repr = to_json.freeze
92
- super
93
- end
94
-
95
- def self.load raw
96
- self[JSON.load raw.to_s].tap do |h|
97
- h.send :repr=, raw
98
- end
99
- end
100
-
101
- private
102
-
103
- def repr= raw
104
- @repr = raw.freeze
105
- freeze
106
- end
107
- end
108
- end
109
-
110
- # Try to convert by detecting token representation and parsing
111
- def self.JWT raw
112
- if raw.is_a? JWT
113
- raw
114
- elsif raw.respond_to?(:to_h) || raw =~ /\A\s*\{/
115
- JWT.parse_json raw
116
- else
117
- JWT.parse_compact raw
118
- end
119
- rescue
120
- raise ArgumentError, "invalid value for JWT(): #{raw.inspect}"
121
- end
122
- end
data/publish.sh DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
-
4
- summon --yaml "RUBYGEMS_API_KEY: !var rubygems/api-key" \
5
- publish-rubygem slosilo
data/secrets.yml DELETED
@@ -1 +0,0 @@
1
- RUBYGEMS_API_KEY: !var rubygems/api-key
@@ -1,114 +0,0 @@
1
- require 'spec_helper'
2
- require 'slosilo/attr_encrypted'
3
-
4
- describe Slosilo::EncryptedAttributes do
5
- before(:all) do
6
- Slosilo::encryption_key = OpenSSL::Cipher.new("aes-256-gcm").random_key
7
- end
8
-
9
- let(:aad) { proc{ |_| "hithere" } }
10
-
11
- let(:base){
12
- Class.new do
13
- attr_accessor :normal_ivar,:with_aad
14
- def stupid_ivar
15
- side_effect!
16
- @_explicit
17
- end
18
- def stupid_ivar= e
19
- side_effect!
20
- @_explicit = e
21
- end
22
- def side_effect!
23
-
24
- end
25
- end
26
- }
27
-
28
- let(:sub){
29
- Class.new(base) do
30
- attr_encrypted :normal_ivar, :stupid_ivar
31
- end
32
- }
33
-
34
- subject{ sub.new }
35
-
36
- context "when setting a normal ivar" do
37
- let(:value){ "some value" }
38
- it "stores an encrypted value in the ivar" do
39
- subject.normal_ivar = value
40
- expect(subject.instance_variable_get(:"@normal_ivar")).to_not eq(value)
41
- end
42
-
43
- it "recovers the value set" do
44
- subject.normal_ivar = value
45
- expect(subject.normal_ivar).to eq(value)
46
- end
47
- end
48
-
49
- context "when setting an attribute with an implementation" do
50
- it "calls the base class method" do
51
- expect(subject).to receive_messages(:side_effect! => nil)
52
- subject.stupid_ivar = "hi"
53
- expect(subject.stupid_ivar).to eq("hi")
54
- end
55
- end
56
-
57
- context "when given an :aad option" do
58
-
59
- let(:cipher){ Slosilo::EncryptedAttributes.cipher }
60
- let(:key){ Slosilo::EncryptedAttributes.key}
61
- context "that is a string" do
62
- let(:aad){ "hello there" }
63
- before{ sub.attr_encrypted :with_aad, aad: aad }
64
- it "encrypts the value with the given string for auth data" do
65
- expect(cipher).to receive(:encrypt).with("hello", key: key, aad: aad)
66
- subject.with_aad = "hello"
67
- end
68
-
69
- it "decrypts the encrypted value" do
70
- subject.with_aad = "foo"
71
- expect(subject.with_aad).to eq("foo")
72
- end
73
- end
74
-
75
- context "that is nil" do
76
- let(:aad){ nil }
77
- before{ sub.attr_encrypted :with_aad, aad: aad }
78
- it "encrypts the value with an empty string for auth data" do
79
- expect(cipher).to receive(:encrypt).with("hello",key: key, aad: "").and_call_original
80
- subject.with_aad = "hello"
81
- end
82
-
83
- it "decrypts the encrypted value" do
84
- subject.with_aad = "hello"
85
- expect(subject.with_aad).to eq("hello")
86
- end
87
- end
88
-
89
- context "that is a proc" do
90
- let(:aad){
91
- proc{ |o| "x" }
92
- }
93
-
94
- before{ sub.attr_encrypted :with_aad, aad: aad }
95
-
96
- it "calls the proc with the object being encrypted" do
97
- expect(aad).to receive(:[]).with(subject).and_call_original
98
- subject.with_aad = "hi"
99
- end
100
-
101
- it "encrypts the value with the string returned for auth data" do
102
- expect(cipher).to receive(:encrypt).with("hello", key: key, aad: aad[subject]).and_call_original
103
- subject.with_aad = "hello"
104
- end
105
- it "decrypts the encrypted value" do
106
- subject.with_aad = "hello"
107
- expect(subject.with_aad).to eq("hello")
108
- end
109
- end
110
-
111
- end
112
-
113
-
114
- end
@@ -1,81 +0,0 @@
1
- require 'spec_helper'
2
- require 'tmpdir'
3
-
4
- require 'slosilo/adapters/file_adapter'
5
-
6
- describe Slosilo::Adapters::FileAdapter do
7
- include_context "with example key"
8
-
9
- let(:dir) { Dir.mktmpdir }
10
- let(:adapter) { Slosilo::Adapters::FileAdapter.new dir }
11
- subject { adapter }
12
-
13
- describe "#get_key" do
14
- context "when given key does not exist" do
15
- it "returns nil" do
16
- expect(subject.get_key(:whatever)).not_to be
17
- end
18
- end
19
- end
20
-
21
- describe "#put_key" do
22
- context "unacceptable id" do
23
- let(:id) { "foo.bar" }
24
- it "isn't accepted" do
25
- expect { subject.put_key id, key }.to raise_error /id should not contain a period/
26
- end
27
- end
28
- context "acceptable id" do
29
- let(:id) { "id" }
30
- let(:key_encrypted) { "encrypted key" }
31
- let(:fname) { "#{dir}/#{id}.key" }
32
- it "creates the key" do
33
- expect(Slosilo::EncryptedAttributes).to receive(:encrypt).with(key.to_der).and_return key_encrypted
34
- expect(File).to receive(:write).with(fname, key_encrypted)
35
- expect(File).to receive(:chmod).with(0400, fname)
36
- subject.put_key id, key
37
- expect(subject.instance_variable_get("@keys")[id]).to eq(key)
38
- end
39
- end
40
- end
41
-
42
- describe "#each" do
43
- before { adapter.instance_variable_set("@keys", one: :onek, two: :twok) }
44
-
45
- it "iterates over each key" do
46
- results = []
47
- adapter.each { |id,k| results << { id => k } }
48
- expect(results).to eq([ { one: :onek}, {two: :twok } ])
49
- end
50
- end
51
-
52
- context 'with real key store' do
53
- let(:id) { 'some id' }
54
-
55
- before do
56
- Slosilo::encryption_key = Slosilo::Symmetric.new.random_key
57
- pre_adapter = Slosilo::Adapters::FileAdapter.new dir
58
- pre_adapter.put_key(id, key)
59
- end
60
-
61
- describe '#get_key' do
62
- it "loads and decrypts the key" do
63
- expect(adapter.get_key(id)).to eq(key)
64
- end
65
- end
66
-
67
- describe '#get_by_fingerprint' do
68
- it "can look up a key by a fingerprint" do
69
- expect(adapter.get_by_fingerprint(key_fingerprint)).to eq([key, id])
70
- end
71
- end
72
-
73
- describe '#each' do
74
- it "enumerates the keys" do
75
- results = []
76
- adapter.each { |id,k| results << { id => k } }
77
- expect(results).to eq([ { id => key } ])
78
- end
79
- end
80
- end
81
- end
data/spec/jwt_spec.rb DELETED
@@ -1,102 +0,0 @@
1
- require 'spec_helper'
2
-
3
- # (Mostly) integration tests for JWT token format
4
- describe Slosilo::Key do
5
- include_context "with example key"
6
-
7
- describe '#issue_jwt' do
8
- it 'issues an JWT token with given claims' do
9
- allow(Time).to receive(:now) { DateTime.parse('2014-06-04 23:22:32 -0400').to_time }
10
-
11
- tok = key.issue_jwt sub: 'host/example', cidr: %w(fec0::/64)
12
-
13
- expect(tok).to be_frozen
14
-
15
- expect(tok.header).to eq \
16
- alg: 'conjur.org/slosilo/v2',
17
- kid: key_fingerprint
18
- expect(tok.claims).to eq \
19
- iat: 1401938552,
20
- sub: 'host/example',
21
- cidr: ['fec0::/64']
22
-
23
- expect(key.verify_signature tok.string_to_sign, tok.signature).to be_truthy
24
- end
25
- end
26
- end
27
-
28
- describe Slosilo::JWT do
29
- context "with a signed token" do
30
- let(:signature) { 'very signed, such alg' }
31
- subject(:token) { Slosilo::JWT.new test: "token" }
32
- before do
33
- allow(Time).to receive(:now) { DateTime.parse('2014-06-04 23:22:32 -0400').to_time }
34
- token.add_signature(alg: 'test-sig') { signature }
35
- end
36
-
37
- it 'allows conversion to JSON representation with #to_json' do
38
- json = JSON.load token.to_json
39
- expect(JSON.load Base64.urlsafe_decode64 json['protected']).to eq \
40
- 'alg' => 'test-sig'
41
- expect(JSON.load Base64.urlsafe_decode64 json['payload']).to eq \
42
- 'iat' => 1401938552, 'test' => 'token'
43
- expect(Base64.urlsafe_decode64 json['signature']).to eq signature
44
- end
45
-
46
- it 'allows conversion to compact representation with #to_s' do
47
- h, c, s = token.to_s.split '.'
48
- expect(JSON.load Base64.urlsafe_decode64 h).to eq \
49
- 'alg' => 'test-sig'
50
- expect(JSON.load Base64.urlsafe_decode64 c).to eq \
51
- 'iat' => 1401938552, 'test' => 'token'
52
- expect(Base64.urlsafe_decode64 s).to eq signature
53
- end
54
- end
55
-
56
- describe '#to_json' do
57
- it "passes any parameters" do
58
- token = Slosilo::JWT.new
59
- allow(token).to receive_messages \
60
- header: :header,
61
- claims: :claims,
62
- signature: :signature
63
- expect_any_instance_of(Hash).to receive(:to_json).with :testing
64
- expect(token.to_json :testing)
65
- end
66
- end
67
-
68
- describe '()' do
69
- include_context "with example key"
70
-
71
- it 'understands both serializations' do
72
- [COMPACT_TOKEN, JSON_TOKEN].each do |token|
73
- token = Slosilo::JWT token
74
- expect(token.header).to eq \
75
- 'typ' => 'JWT',
76
- 'alg' => 'conjur.org/slosilo/v2',
77
- 'kid' => key_fingerprint
78
- expect(token.claims).to eq \
79
- 'sub' => 'host/example',
80
- 'iat' => 1401938552,
81
- 'exp' => 1401938552 + 60*60,
82
- 'cidr' => ['fec0::/64']
83
- expect(key.verify_signature token.string_to_sign, token.signature).to be_truthy
84
- end
85
- end
86
-
87
- it 'is a noop if already parsed' do
88
- token = Slosilo::JWT COMPACT_TOKEN
89
- expect(Slosilo::JWT token).to eq token
90
- end
91
-
92
- it 'raises ArgumentError on failure to convert' do
93
- expect { Slosilo::JWT "foo bar" }.to raise_error ArgumentError
94
- expect { Slosilo::JWT elite: 31337 }.to raise_error ArgumentError
95
- expect { Slosilo::JWT "foo.bar.xyzzy" }.to raise_error ArgumentError
96
- end
97
- end
98
-
99
- COMPACT_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiIxMDdiZGI4NTAxYzQxOWZhZDJmZGIyMGI0NjdkNGQwYTYyYTE2YTk4YzM1ZjJkYTBlYjNiMWZmOTI5Nzk1YWQ5In0=.eyJzdWIiOiJob3N0L2V4YW1wbGUiLCJjaWRyIjpbImZlYzA6Oi82NCJdLCJleHAiOjE0MDE5NDIxNTIsImlhdCI6MTQwMTkzODU1Mn0=.qSxy6gx0DbiIc-Wz_vZhBsYi1SCkHhzxfMGPnnG6MTqjlzy7ntmlU2H92GKGoqCRo6AaNLA_C3hA42PeEarV5nMoTj8XJO_kwhrt2Db2OX4u83VS0_enoztWEZG5s45V0Lv71lVR530j4LD-hpqhm_f4VuISkeH84u0zX7s1zKOlniuZP-abCAHh0htTnrVz9wKG0VywkCUmWYyNNqC2h8PRf64SvCWcQ6VleHpjO-ms8OeTw4ZzRbzKMi0mL6eTmQlbT3PeBArUaS0pNJPg9zdDQaL2XDOofvQmj6Yy_8RA4eCt9HEfTYEdriVqK-_9QCspbGzFVn9GTWf51MRi5dngV9ItsDoG9ktDtqFuMttv7TcqjftsIHZXZsAZ175E".freeze
100
-
101
- JSON_TOKEN = "{\"protected\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiIxMDdiZGI4NTAxYzQxOWZhZDJmZGIyMGI0NjdkNGQwYTYyYTE2YTk4YzM1ZjJkYTBlYjNiMWZmOTI5Nzk1YWQ5In0=\",\"payload\":\"eyJzdWIiOiJob3N0L2V4YW1wbGUiLCJjaWRyIjpbImZlYzA6Oi82NCJdLCJleHAiOjE0MDE5NDIxNTIsImlhdCI6MTQwMTkzODU1Mn0=\",\"signature\":\"qSxy6gx0DbiIc-Wz_vZhBsYi1SCkHhzxfMGPnnG6MTqjlzy7ntmlU2H92GKGoqCRo6AaNLA_C3hA42PeEarV5nMoTj8XJO_kwhrt2Db2OX4u83VS0_enoztWEZG5s45V0Lv71lVR530j4LD-hpqhm_f4VuISkeH84u0zX7s1zKOlniuZP-abCAHh0htTnrVz9wKG0VywkCUmWYyNNqC2h8PRf64SvCWcQ6VleHpjO-ms8OeTw4ZzRbzKMi0mL6eTmQlbT3PeBArUaS0pNJPg9zdDQaL2XDOofvQmj6Yy_8RA4eCt9HEfTYEdriVqK-_9QCspbGzFVn9GTWf51MRi5dngV9ItsDoG9ktDtqFuMttv7TcqjftsIHZXZsAZ175E\"}".freeze
102
- end
data/test.sh DELETED
@@ -1,8 +0,0 @@
1
- #!/bin/bash -xe
2
-
3
-
4
- echo "==> Docker Run"
5
- docker run --rm --volume $PWD:/app --workdir /app cyberark/ubuntu-ruby-builder bash -c 'git config --global --add safe.directory /app && bundle && ls -ltra && bundle exec rake jenkins' || :
6
-
7
- echo "==> CP Coverage to Spec"
8
- cp -r coverage spec