keypairs 0.1.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|