keycard 0.2.4 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +1 -0
- data/bin/rake +29 -0
- data/docs/Makefile +24 -0
- data/docs/_static/.gitkeep +0 -0
- data/docs/_templates/.gitkeep +0 -0
- data/docs/authentication.rst +210 -0
- data/docs/conf.py +46 -0
- data/docs/index.rst +37 -0
- data/docs/requirements.txt +4 -0
- data/docs/runtime_context.rst +42 -0
- data/keycard.gemspec +8 -8
- data/lib/keycard/digest_key.rb +59 -0
- data/lib/keycard/version.rb +1 -1
- data/lib/keycard.rb +1 -0
- metadata +44 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a653bd311fdb43e3c4f202e8761582c459ffa1c4e85a1e7309e337e8dd6e1f0
|
4
|
+
data.tar.gz: 846d43693d28a97cdd4a9fca88e0ee51c6a6289ac389122e078ccc9a8193a40f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b9185d2dc8a350988c52bfd56c3417840969d89167ee7824132ddc505630b99a4bbc757edd1ea8b644e67da856a3ca78fe88a927334d0ecd7cc16c606de423a
|
7
|
+
data.tar.gz: 343cab74ac29ded2ff832c19c141fd0d14dc6fdb7d0da3fb0ba7e32006ba29a0550445a1d42c204d15e569c2797c142a02769e8360cfa9ac8e72c375b2a0f9ad
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
[](https://travis-ci.org/mlibrary/keycard?branch=master)
|
2
2
|
[](https://coveralls.io/github/mlibrary/keycard?branch=master)
|
3
|
+
[](https://keycard.readthedocs.io/en/latest/?badge=latest)
|
3
4
|
|
4
5
|
# Keycard
|
5
6
|
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/docs/Makefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Minimal makefile for Sphinx documentation
|
2
|
+
#
|
3
|
+
|
4
|
+
# You can set these variables from the command line.
|
5
|
+
SPHINXOPTS =
|
6
|
+
SPHINXBUILD = sphinx-build
|
7
|
+
SPHINXPROJ = Keycard
|
8
|
+
AUTOBUILD = sphinx-autobuild
|
9
|
+
SOURCEDIR = .
|
10
|
+
BUILDDIR = _build
|
11
|
+
|
12
|
+
# Put it first so that "make" without argument is like "make help".
|
13
|
+
help:
|
14
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
15
|
+
|
16
|
+
auto:
|
17
|
+
@$(AUTOBUILD) ${@:2} "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
18
|
+
|
19
|
+
.PHONY: auto help Makefile
|
20
|
+
|
21
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
22
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
23
|
+
%: Makefile
|
24
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
File without changes
|
File without changes
|
@@ -0,0 +1,210 @@
|
|
1
|
+
Identity and Authentication
|
2
|
+
===========================
|
3
|
+
|
4
|
+
Users can be identified in any number of ways and carry with them
|
5
|
+
attributes that determine the entirety of "who they are". Typical needs
|
6
|
+
include identifying a person by username or email address, and building a
|
7
|
+
profile of attributes such as geographical region (as determined by IP address),
|
8
|
+
or University status (student, staff, etc.). The identifiers and attributes are
|
9
|
+
intrinsic to the user and do not, by themselves, grant any permissions within
|
10
|
+
an application. Likewise, these attributes cannot be granted within an
|
11
|
+
application, but only inspected.
|
12
|
+
|
13
|
+
This document gives an overview of authentication terminology and scenarios
|
14
|
+
from an application perspective. Not all of these concepts are strictly in the
|
15
|
+
domain of Keycard (for example, we mention some typical authorization rules as
|
16
|
+
they relate to different user types). This background is presented here to
|
17
|
+
establish the environment within which Keycard operates and functionality it
|
18
|
+
seeks to support. How to use Keycard in an application is documented elsewhere
|
19
|
+
(TODO: complete examples and link API).
|
20
|
+
|
21
|
+
Authentication Glossary
|
22
|
+
-----------------------
|
23
|
+
|
24
|
+
There are many overlapping concepts and terms regarding identity, privacy,
|
25
|
+
security, and technology. These definitions give building blocks for the
|
26
|
+
discrete combination scenarios that require (or support) different business
|
27
|
+
rules.
|
28
|
+
|
29
|
+
As far as classifying users, there are three dimensions to consider. A user may
|
30
|
+
have only one status in a given dimension:
|
31
|
+
|
32
|
+
* **Identification** -- Anonymous, Unnamed, Named
|
33
|
+
* **Affiliation** -- Unaffiliated, Affiliated
|
34
|
+
* **Account Status** -- Nonmember, Member
|
35
|
+
|
36
|
+
* **Anonymous** -- individually indistinguishable from other people except
|
37
|
+
circumstantially (e.g., visiting from the same IP address)
|
38
|
+
* **Identified** -- a person whose identity has been verified; importantly, the
|
39
|
+
identity may not be known to all parties
|
40
|
+
* **Unnamed** -- identified, but only opaquely; that is, the person (principal)
|
41
|
+
is not revealed to the application, though the specific affiliation is,
|
42
|
+
usually an organizational role or roles
|
43
|
+
* **Named** -- personally identified; that is, a resolvable identifier for the
|
44
|
+
person (principal) is revealed, possibly along with name information
|
45
|
+
* **Session** -- a period of usage that is considered to be conducted by the
|
46
|
+
same person
|
47
|
+
* **Principal** -- a person with an organizational account (but not necessarily
|
48
|
+
an application account)
|
49
|
+
* **Partner** -- an organization with some agreement with the application
|
50
|
+
providers, in order to authenticate its users and offer access on its behalf
|
51
|
+
* **Affiliate** -- a person with at least one affiliation relationship with a
|
52
|
+
partner; asserted by the organization (e.g., a university asserts that a
|
53
|
+
person is a faculty member)
|
54
|
+
* **Visitor** -- a person with no identification or affiliation whatsoever;
|
55
|
+
effectively unrecognized by any means
|
56
|
+
* **Guest** -- a person with some identification or affiliation; recognized, but
|
57
|
+
transient, that is, not holding an account of any sort
|
58
|
+
* **Member** -- a person with identification; recognized, holding an account;
|
59
|
+
Visitors and Guests are considered non-members
|
60
|
+
|
61
|
+
Not all combinations are sensible for determining business rules. For example,
|
62
|
+
membership requires some form of identification, so there can be no anonymous
|
63
|
+
members.
|
64
|
+
|
65
|
+
To aid consideration of the rules for an application, this table names all of
|
66
|
+
the unique combinations, which are further defined below. It does not imply
|
67
|
+
that all applications should have seven user types, but that each combination
|
68
|
+
has unique characteristics that may need to be handled differently. A given
|
69
|
+
application may exclude certain types or treat different types the same in many
|
70
|
+
ways. For example, an application may completely disregard identification for
|
71
|
+
Guests, treating them all the same.
|
72
|
+
|
73
|
+
.. csv-table::
|
74
|
+
:header: "", "Unaffiliated Nonmember", "Unaffiliated Member", "Affiliated Nonmember", "Affiliated Member"
|
75
|
+
:stub-columns: 1
|
76
|
+
|
77
|
+
"Anonymous", "Visitor (Public)", "--", "Unknown Guest", "--"
|
78
|
+
"Unnamed", "--", "--", "Unnamed Guest", "Private Member"
|
79
|
+
"Named", "--", "Local Account", "Named Guest", "Member"
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
Authentication Scenarios / User Types
|
84
|
+
-------------------------------------
|
85
|
+
|
86
|
+
Visitor
|
87
|
+
~~~~~~~
|
88
|
+
|
89
|
+
A person with no recognized identification or affiliation whatsoever. The user
|
90
|
+
is effectively unauthenticated by any means.
|
91
|
+
|
92
|
+
Anyone who can connect to the application (at the very least by obscuring their
|
93
|
+
identity and any affiliation) may act as a visitor, so there should be no
|
94
|
+
restrictions that apply to other users that do not apply to visitors. Entry or
|
95
|
+
modification of data should be permitted only under scrutiny, where vandalism
|
96
|
+
is of no concern and appropriate auditing and moderation are in place.
|
97
|
+
|
98
|
+
A visitor may have a session to enhance user experience, but all session data
|
99
|
+
should be transient and discarded after an appropriate inactivity period, if
|
100
|
+
stored server-side.
|
101
|
+
|
102
|
+
Visitor (Public)
|
103
|
+
................
|
104
|
+
All Visitors are anonymous and should be treated as "any public user". This
|
105
|
+
typically means that they have access to a limited set of resources and cannot
|
106
|
+
receive any personalization features.
|
107
|
+
|
108
|
+
Guest
|
109
|
+
~~~~~
|
110
|
+
|
111
|
+
A person with some recognized identification or affiliation. The user is
|
112
|
+
authenticated, but transient – that is, not holding an account of any sort.
|
113
|
+
This type of user is typically permitted access to restricted materials based
|
114
|
+
on agreements between organizations.
|
115
|
+
|
116
|
+
There may be useful distinctions applied or features available for different
|
117
|
+
types of Guest. For example, the text may be personalized for a Named Guest, or
|
118
|
+
additional features may be provided to Unnamed Guests with the "faculty"
|
119
|
+
designation.
|
120
|
+
|
121
|
+
Unknown Guest
|
122
|
+
.............
|
123
|
+
A user connecting from a recognized affiliate network, but with no
|
124
|
+
identification. All that can be asserted is that the user has a generic
|
125
|
+
affiliation implying that the person can access organizational computing
|
126
|
+
resources. This may be by way of public computers, campus networking,
|
127
|
+
or VPN access, as examples.
|
128
|
+
|
129
|
+
Unnamed Guest
|
130
|
+
.............
|
131
|
+
A person with specified affiliation and a persistent identifier. The identifier
|
132
|
+
is opaque, but stable; that is, the same identifier presented over time implies
|
133
|
+
that the user is the same person. It is expressly not human-friendly and should
|
134
|
+
not be displayed.
|
135
|
+
|
136
|
+
Keycard calls this persistent ID the user_pid. Generally, the affiliate will
|
137
|
+
not present different identifiers for the same person, but that is outside of
|
138
|
+
the control and knowledge of the application. It should be assumed that
|
139
|
+
different IDs represent different people.
|
140
|
+
|
141
|
+
The affiliation may be multi-valued and is *scoped*, meaning that it applies
|
142
|
+
within a security domain. Common semantics assert that a person has roles like
|
143
|
+
member and staff, or member and student, scoped to the entire affiliate
|
144
|
+
organization. An example of one scoped affiliation would be
|
145
|
+
``faculty@umich.edu``.
|
146
|
+
|
147
|
+
Named Guest
|
148
|
+
...........
|
149
|
+
A person with specified affiliation and both persistent and enterprise
|
150
|
+
identifiers. The persistent identifier is as for Unnamed Guests. The enterprise
|
151
|
+
identifier is name-based, meaning that it based on some account name for the
|
152
|
+
person used within the affiliate organization. It is expressly personally
|
153
|
+
identifiable, and often human-friendly, meaning that other people may recognize
|
154
|
+
it and it would be suitable for display.
|
155
|
+
|
156
|
+
Keycard calls the enterprise ID the ``user_eid``. It is single-valued and
|
157
|
+
often, but not always, matches an email address for the person. Generally, this
|
158
|
+
ID is stable between sessions, but there is no guarantee that it will not be
|
159
|
+
reassigned at some point.
|
160
|
+
|
161
|
+
Member
|
162
|
+
~~~~~~
|
163
|
+
|
164
|
+
A person with recognized identification and an account for application features
|
165
|
+
such as content ownership. The user is authenticated and persistent.
|
166
|
+
|
167
|
+
The reasons to maintain Members may vary between application. For example,
|
168
|
+
those with a narrower audience may prefer the semantics that anyone
|
169
|
+
individually authenticated becomes a Member automatically to simplify data
|
170
|
+
modeling and reporting. Those with very broad audiences may choose to have many
|
171
|
+
Guests and only a few Members to reduce the number of dormant or single-use
|
172
|
+
accounts.
|
173
|
+
|
174
|
+
Local Account
|
175
|
+
.............
|
176
|
+
A user (person or machine user) that is only known the application, not an
|
177
|
+
identity authority. The application must manage any authentication directly.
|
178
|
+
This may not even be an interactive account, but used as a means to record
|
179
|
+
ownership or action by the system consistently alongside human users, for
|
180
|
+
example.
|
181
|
+
|
182
|
+
Some applications may have a dedicated super user with a special login
|
183
|
+
procedure, where others may manage those tasks by designating human Members as
|
184
|
+
administrative users.
|
185
|
+
|
186
|
+
Private Member
|
187
|
+
..............
|
188
|
+
A person with specified affiliation and a privacy-preserving, persistent
|
189
|
+
identifier. This Member is very similar to an Unnamed Guest, but has been given
|
190
|
+
an account for some application purpose. Some applications may choose to have
|
191
|
+
only Unnamed Guests or Private Members, not both types.
|
192
|
+
|
193
|
+
The authentication information does not include anything personally
|
194
|
+
identifiable, so the application must decide whether to ask the user to supply
|
195
|
+
items like a display name or email address, or to deal with the lack of
|
196
|
+
human-friendly information in another way. For example, an application that
|
197
|
+
only maintains a set of favorite items for the user may find no need to provide
|
198
|
+
meaningful display to that member others as to whose favorites they are. By
|
199
|
+
contrast, an application that tracks and attributes comments to a Member would
|
200
|
+
generally need some label for the commenter.
|
201
|
+
|
202
|
+
Member
|
203
|
+
......
|
204
|
+
A person with specified affiliation and both persistent and enterprise
|
205
|
+
identifiers. This Member is similar to a Named Guest, but has been given an
|
206
|
+
account for some application purpose. This Member fits the classical definition
|
207
|
+
of "named user"; that is, account and display information is maintained, likely
|
208
|
+
in order to grant individual permissions and display name information to other
|
209
|
+
users.
|
210
|
+
|
data/docs/conf.py
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
import guzzle_sphinx_theme
|
4
|
+
from recommonmark.parser import CommonMarkParser
|
5
|
+
|
6
|
+
# -- General configuration ------------------------------------------------
|
7
|
+
project = u'Keycard'
|
8
|
+
copyright = u'2018, Regents of the University of Michigan'
|
9
|
+
author = u'Noah Botimer'
|
10
|
+
version = u'0.2.4'
|
11
|
+
release = u'0.2.4'
|
12
|
+
|
13
|
+
|
14
|
+
extensions = ['guzzle_sphinx_theme']
|
15
|
+
templates_path = ['_templates']
|
16
|
+
master_doc = 'index'
|
17
|
+
|
18
|
+
source_parsers = {
|
19
|
+
'.md': CommonMarkParser,
|
20
|
+
}
|
21
|
+
source_suffix = ['.rst', '.md']
|
22
|
+
|
23
|
+
language = None
|
24
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
25
|
+
pygments_style = 'sphinx'
|
26
|
+
todo_include_todos = False
|
27
|
+
|
28
|
+
|
29
|
+
# -- Options for HTML output ----------------------------------------------
|
30
|
+
html_theme_path = guzzle_sphinx_theme.html_theme_path()
|
31
|
+
html_theme = 'guzzle_sphinx_theme'
|
32
|
+
html_static_path = ['_static']
|
33
|
+
|
34
|
+
# Guzzle theme options (see theme.conf for more information)
|
35
|
+
html_theme_options = {
|
36
|
+
"project_nav_name": "Keycard",
|
37
|
+
}
|
38
|
+
|
39
|
+
html_sidebars = {
|
40
|
+
'**': [
|
41
|
+
'logo-text.html',
|
42
|
+
'globaltoc.html',
|
43
|
+
'searchbox.html',
|
44
|
+
]
|
45
|
+
}
|
46
|
+
|
data/docs/index.rst
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
.. title:: Keycard, authentication for Ruby applications
|
2
|
+
|
3
|
+
Keycard Documentation
|
4
|
+
=====================
|
5
|
+
|
6
|
+
Keycard is both a Ruby library and an abstract model for authentication and
|
7
|
+
directory information for users of an application.
|
8
|
+
|
9
|
+
Keycard is primarily concerned with establishing identity and supplemental
|
10
|
+
attributes of users. It provides a data model for user information and
|
11
|
+
conveniences for building applications that will be deployed with reverse
|
12
|
+
proxies and single sign-on systems. It is well-suited to enterprise deployments
|
13
|
+
where there are external login and directory systems.
|
14
|
+
|
15
|
+
Authorization needs are not covered by Keycard at all. See Checkpoint_ for a
|
16
|
+
library that can store grants based on the Keycard attributes and enforce
|
17
|
+
policies against them.
|
18
|
+
|
19
|
+
Table of Contents
|
20
|
+
-----------------
|
21
|
+
|
22
|
+
.. toctree::
|
23
|
+
:maxdepth: 2
|
24
|
+
|
25
|
+
authentication.rst
|
26
|
+
runtime_context.rst
|
27
|
+
|
28
|
+
.. _Checkpoint: https://github.com/mlibrary/checkpoint
|
29
|
+
|
30
|
+
|
31
|
+
Naming
|
32
|
+
------
|
33
|
+
Keycard takes its name from physical keycards, where a person presents a card
|
34
|
+
to a reader. The card may hold any number of attributes, including personal
|
35
|
+
identification, staff classification, or clearance levels. The reader (or
|
36
|
+
attached system) is left to make any authorization decisions or present the
|
37
|
+
information to a person to do so.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Runtime Context
|
2
|
+
===============
|
3
|
+
|
4
|
+
An application can be run in different environments and configurations for
|
5
|
+
different purposes like development, testing, staging, or public use. There are
|
6
|
+
many overlapping terms in use, so, for our purposes here, we use the term
|
7
|
+
*Runtime Context* to give labels to the scenarios where the infrastructure is
|
8
|
+
different, and in what ways.
|
9
|
+
|
10
|
+
Development Context
|
11
|
+
-------------------
|
12
|
+
|
13
|
+
This means that the application is running with direct access by the client,
|
14
|
+
generally from a workstation. There is no front-end server, so all
|
15
|
+
authentication must be managed by the application. The Rails environment is
|
16
|
+
generally set to ``development``.
|
17
|
+
|
18
|
+
A proxy managing single sign-on may be emulated either at the Rack or
|
19
|
+
application level if needed. Generally, there is some bit of UI or cookie/param
|
20
|
+
handling exposed only in development mode to influence how the requests appear
|
21
|
+
to the application, or there is a login form for local accounts.
|
22
|
+
|
23
|
+
Deployment Context
|
24
|
+
------------------
|
25
|
+
|
26
|
+
This means that the application is deployed to dedicated infrastructure. There
|
27
|
+
is a front-end server (proxy) that may or may not manage single sign-on. The
|
28
|
+
Rails environment is generally set to ``production``.
|
29
|
+
|
30
|
+
A typical configuration is to have an Apache web server proxying all traffic,
|
31
|
+
with either a module for Cosign, CAS, or Shibboleth configured. There is often
|
32
|
+
a fixed path (e.g., ``/login``) that is intercepted to require SSO
|
33
|
+
authentication. If the user is authenticated, the request is forwarded on with
|
34
|
+
headers in place. If the user cannot authenticate, the app never receives the
|
35
|
+
login path request. For Shibboleth scenarios, there is a Service Provider that
|
36
|
+
is set up for the endpoint (application URL).
|
37
|
+
|
38
|
+
With Shibboleth, it is also possible to have the headers present on each
|
39
|
+
request when there is an active session with the Service Provider. Some special
|
40
|
+
care must be taken here that sessions are initiated and terminated properly and
|
41
|
+
when desired (usually on login and logout requests).
|
42
|
+
|
data/keycard.gemspec
CHANGED
@@ -26,14 +26,14 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.add_dependency "sequel"
|
28
28
|
|
29
|
-
spec.add_development_dependency "bundler"
|
30
|
-
spec.add_development_dependency "coveralls"
|
29
|
+
spec.add_development_dependency "bundler"
|
30
|
+
spec.add_development_dependency "coveralls"
|
31
31
|
spec.add_development_dependency "pry"
|
32
|
-
spec.add_development_dependency "rake"
|
33
|
-
spec.add_development_dependency "rspec"
|
34
|
-
spec.add_development_dependency "rubocop"
|
35
|
-
spec.add_development_dependency "rubocop-rails"
|
36
|
-
spec.add_development_dependency "rubocop-rspec"
|
32
|
+
spec.add_development_dependency "rake"
|
33
|
+
spec.add_development_dependency "rspec"
|
34
|
+
spec.add_development_dependency "rubocop"
|
35
|
+
spec.add_development_dependency "rubocop-rails"
|
36
|
+
spec.add_development_dependency "rubocop-rspec"
|
37
37
|
spec.add_development_dependency "sqlite3"
|
38
|
-
spec.add_development_dependency "yard"
|
38
|
+
spec.add_development_dependency "yard"
|
39
39
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
require "securerandom"
|
5
|
+
|
6
|
+
# A typical digest or api key, ready to be encrypted.
|
7
|
+
class Keycard::DigestKey
|
8
|
+
class HiddenKeyError < StandardError; end
|
9
|
+
HIDDEN_KEY = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".freeze
|
10
|
+
|
11
|
+
# To simply mint a new key, call #new without any parameters.
|
12
|
+
# For wrapping existing, deserialized keys, pass the digest to the constructor.
|
13
|
+
# @param digest [String] The value of the hashed key
|
14
|
+
# @param key [String] Use this if you'd like to specify the unhashed key.
|
15
|
+
# If a digest is also provided, this parameter is ignored.
|
16
|
+
def initialize(digest = nil, key: nil)
|
17
|
+
if digest
|
18
|
+
@digest = digest
|
19
|
+
else
|
20
|
+
@key = key || SecureRandom.uuid
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# A string representation of this key. For hidden keys, this returns an
|
25
|
+
# obfuscated value.
|
26
|
+
# @return [String]
|
27
|
+
def to_s
|
28
|
+
@key || HIDDEN_KEY
|
29
|
+
end
|
30
|
+
|
31
|
+
# The unhashed value of the key.
|
32
|
+
# @return [String]
|
33
|
+
# @raise [HiddenKeyError] This exception is raised if the unhashed key is
|
34
|
+
# not available.
|
35
|
+
def value
|
36
|
+
if @key
|
37
|
+
@key
|
38
|
+
else
|
39
|
+
raise HiddenKeyError, "Cannot display hashed/hidden keys"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# The result of hashing the key
|
44
|
+
# @return [String]
|
45
|
+
def digest
|
46
|
+
@digest ||= Digest::SHA256.hexdigest(@key)
|
47
|
+
end
|
48
|
+
|
49
|
+
def eql?(other)
|
50
|
+
digest == if other.is_a?(self.class)
|
51
|
+
other.digest
|
52
|
+
else
|
53
|
+
other.to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
alias == eql?
|
57
|
+
|
58
|
+
end
|
59
|
+
|
data/lib/keycard/version.rb
CHANGED
data/lib/keycard.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keycard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Botimer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2019-03-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sequel
|
@@ -29,30 +29,30 @@ dependencies:
|
|
29
29
|
name: bundler
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - "
|
39
|
+
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
41
|
+
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: coveralls
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0
|
48
|
+
version: '0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - "
|
53
|
+
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0
|
55
|
+
version: '0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: pry
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,72 +71,72 @@ dependencies:
|
|
71
71
|
name: rake
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - "
|
74
|
+
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version: '
|
76
|
+
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - "
|
81
|
+
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
83
|
+
version: '0'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: rspec
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- - "
|
88
|
+
- - ">="
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: '
|
90
|
+
version: '0'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- - "
|
95
|
+
- - ">="
|
96
96
|
- !ruby/object:Gem::Version
|
97
|
-
version: '
|
97
|
+
version: '0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: rubocop
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- - "
|
102
|
+
- - ">="
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: '0
|
104
|
+
version: '0'
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- - "
|
109
|
+
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: '0
|
111
|
+
version: '0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: rubocop-rails
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
115
115
|
requirements:
|
116
|
-
- - "
|
116
|
+
- - ">="
|
117
117
|
- !ruby/object:Gem::Version
|
118
|
-
version: '
|
118
|
+
version: '0'
|
119
119
|
type: :development
|
120
120
|
prerelease: false
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
122
122
|
requirements:
|
123
|
-
- - "
|
123
|
+
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version: '
|
125
|
+
version: '0'
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: rubocop-rspec
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
|
-
- - "
|
130
|
+
- - ">="
|
131
131
|
- !ruby/object:Gem::Version
|
132
|
-
version: '
|
132
|
+
version: '0'
|
133
133
|
type: :development
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
136
136
|
requirements:
|
137
|
-
- - "
|
137
|
+
- - ">="
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version: '
|
139
|
+
version: '0'
|
140
140
|
- !ruby/object:Gem::Dependency
|
141
141
|
name: sqlite3
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
@@ -155,16 +155,16 @@ dependencies:
|
|
155
155
|
name: yard
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
157
157
|
requirements:
|
158
|
-
- - "
|
158
|
+
- - ">="
|
159
159
|
- !ruby/object:Gem::Version
|
160
|
-
version: '0
|
160
|
+
version: '0'
|
161
161
|
type: :development
|
162
162
|
prerelease: false
|
163
163
|
version_requirements: !ruby/object:Gem::Requirement
|
164
164
|
requirements:
|
165
|
-
- - "
|
165
|
+
- - ">="
|
166
166
|
- !ruby/object:Gem::Version
|
167
|
-
version: '0
|
167
|
+
version: '0'
|
168
168
|
description:
|
169
169
|
email:
|
170
170
|
- botimer@umich.edu
|
@@ -183,11 +183,21 @@ files:
|
|
183
183
|
- README.md
|
184
184
|
- Rakefile
|
185
185
|
- bin/console
|
186
|
+
- bin/rake
|
186
187
|
- bin/setup
|
187
188
|
- db/migrations/1_create_tables.rb
|
189
|
+
- docs/Makefile
|
190
|
+
- docs/_static/.gitkeep
|
191
|
+
- docs/_templates/.gitkeep
|
192
|
+
- docs/authentication.rst
|
193
|
+
- docs/conf.py
|
194
|
+
- docs/index.rst
|
195
|
+
- docs/requirements.txt
|
196
|
+
- docs/runtime_context.rst
|
188
197
|
- keycard.gemspec
|
189
198
|
- lib/keycard.rb
|
190
199
|
- lib/keycard/db.rb
|
200
|
+
- lib/keycard/digest_key.rb
|
191
201
|
- lib/keycard/institution_finder.rb
|
192
202
|
- lib/keycard/railtie.rb
|
193
203
|
- lib/keycard/request.rb
|