keypairs 0.1.0.alpha.1
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +57 -0
- data/app/controllers/keypairs/public_keys_controller.rb +24 -0
- data/app/models/keypair.rb +175 -0
- data/db/migrate/20201024100500_create_keypairs.rb +15 -0
- data/lib/keypairs.rb +3 -0
- data/lib/keypairs/engine.rb +18 -0
- data/lib/keypairs/version.rb +5 -0
- metadata +249 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c6674e2a0a6b9e2a995950c220252b13402d1410d58d2102819cf10811275a50
|
4
|
+
data.tar.gz: dfd6fa3397cbce88a13b405fcf5c4f6fea0dfa20fcd94daba6d7247f0c33aca9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c92758e1f8552b5cc461790e7779a2526d21580a962c8de2126ed441f19a548273fe049924dbf1e80e92edb08becb84fdb00a7877142a9cfcd600bc474df1977
|
7
|
+
data.tar.gz: d689d8a570e11ff872174a5163411427f9cd189fff972976741bae5c89b01624096f3646bb6194257e8698d6c3e7fe554faf20726b6992bc011e37a6094b7353
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Stef Schenkelaars
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Keypairs
|
2
|
+
Applications often need to have a public/private keypair so sign messages. This gem manages your application level key pairs with automatic rotation and support for encoding and decoding [JWTs](https://jwt.io/).
|
3
|
+
|
4
|
+
Note: This gem is intended to work within Rails applications. It can probably be adjusted easily to also work for non-rails / sinatra project but that's out of scope for now.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'keypairs'
|
11
|
+
```
|
12
|
+
|
13
|
+
The of course run `bundle install` and run the migrations `bundle exec rake db:migrate`. The migrations from the gem run automatically.
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
The central point of this gem is the `Keypair` model which is backed by the `keypairs` table. If you need to sign messages, you can get the current keypair with the `Keypair.current` method. This method performs the rotation of the keypairs if required.
|
17
|
+
|
18
|
+
You can access the private an public key of the keypair (`OpenSSL::PKey::RSA`) and encrypt and decrypt messages with them:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
encoded_message = Keypair.current.private_key.private_decrypt('foobar')
|
22
|
+
Keypair.current.public_key.public_decrypt(encoded_message)
|
23
|
+
# => 'foobar'
|
24
|
+
```
|
25
|
+
|
26
|
+
### JWT support
|
27
|
+
You can encode and decode JWTs directly on the class:
|
28
|
+
```ruby
|
29
|
+
payload = { foo: 'bar' }
|
30
|
+
id_token = Keypair.jwt_encode(payload)
|
31
|
+
decoded = Keypair.jwt_decode(id_token)
|
32
|
+
```
|
33
|
+
|
34
|
+
It's almost always a good idea to add a subject to your payload and pass the same subject during decoding. That way you know that users don't use a key for other purposes (for example a key intended for an OAuth2 flow used as a session key). So for example:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
subject = 'MyAppSession'
|
38
|
+
payload = { foo: 'bar', subject: subject }
|
39
|
+
id_token = Keypair.jwt_encode(payload)
|
40
|
+
decoded = Keypair.jwt_decode(id_token, subject: subject)
|
41
|
+
```
|
42
|
+
|
43
|
+
### Exposing public keys
|
44
|
+
If you want others to validate your messages based on the public keys, you can share the JWK version of you current keys by adding them to your `config/routes.rb`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
get :jwks, to: Keypairs::PublicKeysController.action(:index)
|
48
|
+
```
|
49
|
+
|
50
|
+
## Releasing new version
|
51
|
+
Publishing a new version is handled by the publish workflow. This workflow publishes a GitHub release to rubygems and GitHub package registry with the version defined in the release.
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Drieam/keypairs.
|
55
|
+
|
56
|
+
## License
|
57
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Keypairs
|
4
|
+
# Endpoint to fetch the current valid keypairs.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# {
|
8
|
+
# "keys": [
|
9
|
+
# {
|
10
|
+
# "kty": "RSA",
|
11
|
+
# "n": "wmi......1Gw",
|
12
|
+
# "e": "AQAB",
|
13
|
+
# "kid": "d8d1d4265d6c34acadce8a42fbbec167db1beaeb6ebbbf7fd555f6eb00bda76e",
|
14
|
+
# "alg": "RS256",
|
15
|
+
# "use": "sig"
|
16
|
+
# }
|
17
|
+
# ]
|
18
|
+
# }
|
19
|
+
class PublicKeysController < ActionController::API
|
20
|
+
def index
|
21
|
+
render json: Keypair.keyset
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'attr_encrypted'
|
4
|
+
require 'jwt'
|
5
|
+
|
6
|
+
# This class contains functionality needed for signing messages
|
7
|
+
# and publishing JWK[s].
|
8
|
+
#
|
9
|
+
# The last three created keypairs are considered valid, so creating a new Keypair
|
10
|
+
# will invalidate the second to last created Keypair.
|
11
|
+
#
|
12
|
+
# If you need to sign messages, use the {Keypair.current} keypair for this. This method
|
13
|
+
# performs the rotation of the keypairs if required.
|
14
|
+
#
|
15
|
+
# You can also use the +jwt_encode+ and +jwt_decode+ methods directly to encode and
|
16
|
+
# securely decode your payloads
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# payload = { foo: 'bar' }
|
20
|
+
# id_token = Keypair.jwt_encode(payload)
|
21
|
+
# decoded = Keypair.jwt_decode(id_token)
|
22
|
+
#
|
23
|
+
# @attr [String] jwk_kid The public external id of the key used to find the associated key on decoding.
|
24
|
+
class Keypair < ActiveRecord::Base
|
25
|
+
ALGORITHM = 'RS256'
|
26
|
+
|
27
|
+
attr_encrypted :_keypair, key: Rails.application.secrets.secret_key_base[0, 32]
|
28
|
+
|
29
|
+
validates :_keypair, presence: true
|
30
|
+
validates :jwk_kid, presence: true
|
31
|
+
|
32
|
+
after_initialize :set_keypair
|
33
|
+
|
34
|
+
# @!method valid
|
35
|
+
# @!scope class
|
36
|
+
# The last 3 keypairs are considered valid and can be used to validate signatures and export public jwks.
|
37
|
+
# It uses a subquery to make sure a +find_by+ actually searches only the valid 3 ones.
|
38
|
+
scope :valid, -> { where(id: unscoped.order(created_at: :desc).limit(3)) }
|
39
|
+
|
40
|
+
# @return [Keypair] the keypair used to sign messages and autorotates if it is older than 1 month.
|
41
|
+
def self.current
|
42
|
+
order(:created_at).where(arel_table[:created_at].gt(1.month.ago)).last || create!
|
43
|
+
end
|
44
|
+
|
45
|
+
# The JWK Set of our valid keypairs.
|
46
|
+
# @return [Hash]
|
47
|
+
# @example
|
48
|
+
# {
|
49
|
+
# keys: [{
|
50
|
+
# e: "AQAB",
|
51
|
+
# use: "sig",
|
52
|
+
# alg: "RS256",
|
53
|
+
# kty: "RSA",
|
54
|
+
# n: "oNqXxxWuX7LlovO5reRNauF6TEFa-RRRl8Dw==...",
|
55
|
+
# kid: "1516918956_0"
|
56
|
+
# }, {
|
57
|
+
# e: "AQAB",
|
58
|
+
# use: "sig",
|
59
|
+
# alg: "RS256",
|
60
|
+
# kty: "RSA",
|
61
|
+
# n: "kMfHwTp2dIYybtvU-xzF2E3dRJBNm6g5kTQi8itw==...",
|
62
|
+
# kid: "1516918956_1"
|
63
|
+
# }]
|
64
|
+
# }
|
65
|
+
#
|
66
|
+
# @see https://www.imsglobal.org/spec/security/v1p0/#h_key-set-url
|
67
|
+
def self.keyset
|
68
|
+
{
|
69
|
+
keys: valid.order(created_at: :desc).map(&:public_jwk_export)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Encodes the payload with the current keypair.
|
74
|
+
# It forewards the call to the instance method {Keypair#jwt_encode}.
|
75
|
+
# @return [String] Encoded JWT token with security credentials.
|
76
|
+
# @param payload [Hash] Hash which should be encoded.
|
77
|
+
def self.jwt_encode(payload)
|
78
|
+
current.jwt_encode(payload)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Decodes the payload and verifies the signature against the current valid keypairs.
|
82
|
+
# @param id_token [String] A JWT that should be decoded.
|
83
|
+
# @param options [Hash] options for decoding, passed to {JWT::Decode}.
|
84
|
+
# @raise [JWT::DecodeError] or any of it's subclasses if the decoding / validation fails.
|
85
|
+
# @return [Hash] Decoded payload hash with indifferent access.
|
86
|
+
def self.jwt_decode(id_token, options = {})
|
87
|
+
# Add default decoding options
|
88
|
+
options.reverse_merge!(
|
89
|
+
# Change the default algorithm to match the encoding algorithm
|
90
|
+
algorithm: ALGORITHM,
|
91
|
+
# Load our own keyset as valid keys
|
92
|
+
jwks: keyset,
|
93
|
+
# If the `sub` is provided, validate that it matches the payload `sub`
|
94
|
+
verify_sub: true
|
95
|
+
)
|
96
|
+
JWT.decode(id_token, nil, true, options).first.with_indifferent_access
|
97
|
+
end
|
98
|
+
|
99
|
+
# JWT encodes the payload with this keypair.
|
100
|
+
# It automatically adds the security attributes +iat+, +exp+ and +nonce+ to the payload.
|
101
|
+
# It automatically sets the +kid+ in the header.
|
102
|
+
# @param payload [Hash] you have to provide a hash since the security attributes have to be added.
|
103
|
+
# @param headers [Hash] you can optionally add additional headers to the JWT.
|
104
|
+
def jwt_encode(payload, headers = {})
|
105
|
+
# Add security claims to payload
|
106
|
+
payload.reverse_merge!(
|
107
|
+
# Time at which the Issuer generated the JWT (epoch).
|
108
|
+
iat: Time.now.to_i,
|
109
|
+
|
110
|
+
# Expiration time on or after which the tool MUST NOT accept the ID Token for
|
111
|
+
# processing (epoch). This is mostly used to allow some clock skew.
|
112
|
+
exp: Time.now.to_i + 5.minutes.to_i,
|
113
|
+
|
114
|
+
# String value used to associate a tool session with an ID Token, and to mitigate replay
|
115
|
+
# attacks. The nonce value is a case-sensitive string.
|
116
|
+
nonce: SecureRandom.uuid
|
117
|
+
)
|
118
|
+
|
119
|
+
# Add additional info into the headers
|
120
|
+
headers.reverse_merge!(
|
121
|
+
# Set the id of they key
|
122
|
+
kid: jwk_kid
|
123
|
+
)
|
124
|
+
|
125
|
+
JWT.encode(payload, private_key, ALGORITHM, headers)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Public representation of the keypair in the JWK format.
|
129
|
+
# We append the +alg+, and +use+ parameters to our JWK to indicate
|
130
|
+
# that our intended use is to generate signatures using +RS256+.
|
131
|
+
#
|
132
|
+
# +alg+::
|
133
|
+
# This (algorithm) parameter identifies the algorithm intended for use with the key.
|
134
|
+
# It is based in the {Keypair::ALGORITHM}.
|
135
|
+
# The IMS Security framework specifies that the +alg+ value SHOULD be the default of +RS256+.
|
136
|
+
# Use of this member is OPTIONAL.
|
137
|
+
# +use+::
|
138
|
+
# This (public key use) parameter identifies the intended use of the public key.
|
139
|
+
# Use of this member is OPTIONAL, unless the application requires its presence.
|
140
|
+
#
|
141
|
+
# @see https://tools.ietf.org/html/rfc7517#section-4.4
|
142
|
+
# @see https://www.imsglobal.org/spec/security/v1p0#authentication-response-validation
|
143
|
+
def public_jwk_export
|
144
|
+
public_jwk.export.merge(
|
145
|
+
alg: ALGORITHM,
|
146
|
+
use: 'sig'
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
# @return [OpenSSL::PKey::RSA] {OpenSSL::PKey::RSA} instance loaded with our keypair.
|
151
|
+
def private_key
|
152
|
+
OpenSSL::PKey::RSA.new(_keypair)
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [OpenSSL::PKey::RSA] {OpenSSL::PKey::RSA} instance loaded with the public part our keypair.
|
156
|
+
delegate :public_key, to: :private_key
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# @return [JWT::JWK] {JWT::JWK} instance with the public part of our keypair.
|
161
|
+
def public_jwk
|
162
|
+
JWT::JWK.create_from(public_key)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Generate a new keypair with a key_size of 2048. Keys less than 1024 bits should be
|
166
|
+
# considered insecure.
|
167
|
+
#
|
168
|
+
# See:
|
169
|
+
# https://ruby-doc.org/stdlib-2.6.5/libdoc/openssl/rdoc/OpenSSL/PKey/RSA.html#method-c-new
|
170
|
+
def set_keypair
|
171
|
+
# The generated keypair is stored in PEM encoding.
|
172
|
+
self._keypair ||= OpenSSL::PKey::RSA.new(2048).to_pem
|
173
|
+
self.jwk_kid = public_jwk.kid
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateKeypairs < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
create_table :keypairs do |t|
|
6
|
+
t.string :jwk_kid, null: false
|
7
|
+
t.string :encrypted__keypair, null: false
|
8
|
+
t.string :encrypted__keypair_iv, null: false
|
9
|
+
t.timestamps precision: 6
|
10
|
+
# Since we are ordering on created_at, let's create an index
|
11
|
+
t.index :created_at
|
12
|
+
t.index :jwk_kid, unique: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/keypairs.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Keypairs
|
4
|
+
# Rails engine for this gem.
|
5
|
+
# It ensures that the migrations are automatically ran in the applications.
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
initializer :append_migrations do |app|
|
8
|
+
unless app.root.to_s.match? "#{root}/"
|
9
|
+
config.paths['db/migrate'].expanded.each do |expanded_path|
|
10
|
+
app.config.paths['db/migrate'] << expanded_path
|
11
|
+
end
|
12
|
+
# Apartment will modify this, but it doesn't fully support engine migrations,
|
13
|
+
# so we'll reset it here
|
14
|
+
ActiveRecord::Migrator.migrations_paths = app.paths['db/migrate'].to_a
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: keypairs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.alpha.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stef Schenkelaars
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-10-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: attr_encrypted
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jwt
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: brakeman
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: combustion
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: database_cleaner-active_record
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec-github
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec-rails
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-performance
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rubocop-rails
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: shoulda-matchers
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: sqlite3
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
description: Manage application level keypairs with automatic rotation and JWT support
|
210
|
+
email:
|
211
|
+
- stef.schenkelaars@gmail.com
|
212
|
+
executables: []
|
213
|
+
extensions: []
|
214
|
+
extra_rdoc_files: []
|
215
|
+
files:
|
216
|
+
- LICENSE
|
217
|
+
- README.md
|
218
|
+
- app/controllers/keypairs/public_keys_controller.rb
|
219
|
+
- app/models/keypair.rb
|
220
|
+
- db/migrate/20201024100500_create_keypairs.rb
|
221
|
+
- lib/keypairs.rb
|
222
|
+
- lib/keypairs/engine.rb
|
223
|
+
- lib/keypairs/version.rb
|
224
|
+
homepage: https://drieam.github.io/keypairs
|
225
|
+
licenses:
|
226
|
+
- MIT
|
227
|
+
metadata:
|
228
|
+
homepage_uri: https://drieam.github.io/keypairs
|
229
|
+
source_code_uri: https://github.com/Drieam/keypairs
|
230
|
+
post_install_message:
|
231
|
+
rdoc_options: []
|
232
|
+
require_paths:
|
233
|
+
- lib
|
234
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
235
|
+
requirements:
|
236
|
+
- - ">="
|
237
|
+
- !ruby/object:Gem::Version
|
238
|
+
version: 2.5.0
|
239
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: 1.3.1
|
244
|
+
requirements: []
|
245
|
+
rubygems_version: 3.1.4
|
246
|
+
signing_key:
|
247
|
+
specification_version: 4
|
248
|
+
summary: Manage application level keypairs with automatic rotation and JWT support
|
249
|
+
test_files: []
|