keycard 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/mlibrary/keycard.svg?branch=master)](https://travis-ci.org/mlibrary/keycard?branch=master)
|
2
2
|
[![Coverage Status](https://coveralls.io/repos/github/mlibrary/keycard/badge.svg?branch=master)](https://coveralls.io/github/mlibrary/keycard?branch=master)
|
3
|
+
[![Documentation Status](https://readthedocs.org/projects/keycard/badge/?version=latest)](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
|