keycard 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f5d7f477660b581edd56242cba917ceac9a92c56
4
+ data.tar.gz: 64257be7f76d1497ca3f8397e18e6c56d7e49269
5
+ SHA512:
6
+ metadata.gz: 6105dc8009f59f487d8f463d97325ef021f2f922ada9d912cdf6b83d193efe1c9d7cbf86af1c9c0fb3c3959b50dda8ef73dcb55162e61e7b41a091f08f1ff0e7
7
+ data.tar.gz: c55623373226fffa991ac9ef370a71085ca2da258080ab3ea6046788ebc9f691eaac5bd1bed5c2029ab6ea8131f608981ef9a9e8c781ca20a643824bf260e73d
data/.envrc ADDED
@@ -0,0 +1 @@
1
+ PATH_add bin
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ Gemfile.lock
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
14
+
15
+ # Keycard database files
16
+ /db/*.sqlite
17
+ /db/keycard.log
18
+ /db/keycard.yml
19
+
20
+ keycard*.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,30 @@
1
+ Rails:
2
+ Enabled: true
3
+
4
+ Rails/Delegate:
5
+ Enabled: false
6
+
7
+ # inherit_gem:
8
+ # rubocop-rails:
9
+ # - config/rails.yml
10
+
11
+ AllCops:
12
+ DisplayCopNames: true
13
+ TargetRubyVersion: 2.4
14
+ Exclude:
15
+ - 'bin/**/*'
16
+ - 'vendor/**/*'
17
+
18
+ Layout/MultilineMethodDefinitionBraceLayout:
19
+ EnforcedStyle: same_line
20
+
21
+ Metrics/LineLength:
22
+ Max: 110
23
+
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - '*.gemspec'
27
+ ExcludedMethods: ['describe', 'context']
28
+
29
+ Style/StringLiterals:
30
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.2
5
+ before_install: gem install bundler -v 1.16.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in keycard.gemspec
8
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2018, The Regents of the University of Michigan.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+ * Neither the name of the The University of Michigan nor the
14
+ names of its contributors may be used to endorse or promote products
15
+ derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF MICHIGAN AND
18
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE
21
+ UNIVERSITY OF MICHIGAN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23
+ TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ [![Build Status](https://travis-ci.org/mlibrary/keycard.svg?branch=master)](https://travis-ci.org/mlibrary/keycard?branch=master)
2
+ [![Coverage Status](https://coveralls.io/repos/github/mlibrary/keycard/badge.svg?branch=master)](https://coveralls.io/github/mlibrary/keycard?branch=master)
3
+
4
+ # Keycard
5
+
6
+ Keycard provides authentication support and user/request information, especially
7
+ in Rails applications.
8
+
9
+ Keycard is designed to give you sound guidelines and integration between
10
+ authentication and authorization without constraining your application. It
11
+ takes inspiration from [Sorcery](https://github.com/Sorcery/sorcery), but has
12
+ four important distinctions:
13
+
14
+ 1. It does not use mixins to configure a "model that can log in".
15
+ 2. It provides a way to retrieve user and session attributes like directory
16
+ information or IP address-based region, rather than being strictly about
17
+ logging in and out.
18
+ 3. It only provides one built-in strategy for logins, focused on single sign-on
19
+ scenarios.
20
+ 4. It offers an optional group implementation for whatever objects your
21
+ application manages as accounts or users.
22
+
23
+ The ultimate goal is to provide useful tools that integrate easily and simplify
24
+ building applications that have clean, well-factored designs. Keycard should
25
+ help you focus on solving your application problems, while remaining invisible
26
+ -- not magical -- to most of your application.
27
+
28
+ ## Installation
29
+
30
+ Add this line to your application's Gemfile:
31
+
32
+ ```ruby
33
+ gem 'keycard'
34
+ ```
35
+
36
+ And then execute:
37
+
38
+ $ bundle
39
+
40
+ Or install it yourself as:
41
+
42
+ $ gem install keycard
43
+
44
+ ## License
45
+
46
+ Keycard is licensed under the BSD-3-Clause license. See [LICENSE.md](LICENSE.md).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "keycard"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
4
+
5
+ Sequel.migration do
6
+ change do
7
+ create_table(:aa_inst) do
8
+ Integer :uniqueIdentifier, null: false
9
+ String :organizationName, size: 128, null: false
10
+ Integer :manager
11
+ DateTime :lastModifiedTime, default: Sequel::CURRENT_TIMESTAMP, null: false
12
+ String :lastModifiedBy, size: 64, null: false
13
+ String :dlpsDeleted, size: 1, fixed: true, null: false
14
+
15
+ primary_key [:uniqueIdentifier]
16
+ end
17
+
18
+ create_table(:aa_network, ignore_index_errors: true) do
19
+ Integer :uniqueIdentifier, null: false
20
+ String :dlpsDNSName, size: 128
21
+ String :dlpsCIDRAddress, size: 18
22
+ Bignum :dlpsAddressStart
23
+ Bignum :dlpsAddressEnd
24
+ String :dlpsAccessSwitch, size: 5, null: false
25
+ String :coll, size: 32
26
+ Integer :inst
27
+ DateTime :lastModifiedTime, default: Sequel::CURRENT_TIMESTAMP, null: false
28
+ String :lastModifiedBy, size: 64, null: false
29
+ String :dlpsDeleted, size: 1, fixed: true, null: false
30
+
31
+ check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:dlpsAddressStart), 0)
32
+ check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:dlpsAddressEnd), 0)
33
+ primary_key [:uniqueIdentifier]
34
+
35
+ index [:dlpsAddressEnd], name: :network_dlpsAddressEnd_index
36
+ index [:dlpsAddressStart], name: :network_dlpsAddressStart_index
37
+ end
38
+ end
39
+ end
40
+
41
+ # rubocop:enable Metrics/BlockLength
data/keycard.gemspec ADDED
@@ -0,0 +1,41 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path("../lib", __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require "keycard/version"
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "keycard"
10
+ spec.version = Keycard::VERSION
11
+ spec.authors = ["Noah Botimer", "Aaron Elkiss"]
12
+ spec.email = ["botimer@umich.edu", "aelkiss@umich.edu"]
13
+ spec.license = "BSD-3-Clause"
14
+
15
+ spec.summary = <<~SUMMARY
16
+ Keycard provides authentication support and user/request information,
17
+ especially in Rails applications.
18
+ SUMMARY
19
+ spec.homepage = "https://github.com/mlibrary/keycard"
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "mysql2"
29
+ spec.add_dependency "sequel"
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.16"
32
+ spec.add_development_dependency "coveralls", "~> 0.8"
33
+ spec.add_development_dependency "pry"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_development_dependency "rubocop", "~> 0.52"
37
+ spec.add_development_dependency "rubocop-rails", "~> 1.1"
38
+ spec.add_development_dependency "rubocop-rspec", "~> 1.16"
39
+ spec.add_development_dependency "sqlite3"
40
+ spec.add_development_dependency "yard", "~> 0.9"
41
+ end
data/lib/keycard.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "keycard/version"
4
+ require "sequel"
5
+
6
+ # All of the Keycard components are contained within this top-level module.
7
+ module Keycard
8
+ end
9
+
10
+ require "keycard/db"
11
+ require "keycard/railtie" if defined?(Rails)
12
+ require "keycard/request_attributes"
13
+ require "keycard/institution_finder"
data/lib/keycard/db.rb ADDED
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+ require 'logger'
5
+ require 'yaml'
6
+
7
+ module Keycard
8
+ # Module for database interactions for Keycard.
9
+ module DB
10
+ # Any error with the database that Keycard itself detects but cannot handle.
11
+ class DatabaseError < StandardError; end
12
+
13
+ CONNECTION_ERROR = 'The Keycard database is not initialized. Call initialize! first.'
14
+
15
+ ALREADY_CONNECTED = 'Already connected; refusing to connect to another database.'
16
+
17
+ MISSING_CONFIG = <<~MSG
18
+ KEYCARD_DATABASE_URL and DATABASE_URL are both missing and a connection
19
+ has not been configured. Cannot connect to the Keycard database.
20
+ See Keycard::DB.connect! for help.
21
+ MSG
22
+
23
+ LOAD_ERROR = <<~MSG
24
+ Error loading Keycard database models.
25
+ Verify connection information and that the database is migrated.
26
+ MSG
27
+
28
+ SCHEMA_HEADER = "# Keycard Database Version\n"
29
+
30
+ class << self
31
+ # Initialize Keycard
32
+ #
33
+ # This connects to the database if it has not already happened and
34
+ # requires all of the Keycard model classes. It is required to do the
35
+ # connection setup first because of the design decision in Sequel that
36
+ # the schema is examined at the time of extending Sequel::Model.
37
+ def initialize!
38
+ connect! unless connected?
39
+ begin
40
+ model_files.each do |file|
41
+ require_relative file
42
+ end
43
+ rescue Sequel::DatabaseError, NoMethodError => e
44
+ raise DatabaseError, LOAD_ERROR + "\n" + e.message
45
+ end
46
+ db
47
+ end
48
+
49
+ # Connect to the Keycard database.
50
+ #
51
+ # The default is to use the settings under {.config}, but can be
52
+ # supplied here (and they will be merged into config as a side effect).
53
+ # The keys that will be used from either source are documented here as
54
+ # the options.
55
+ #
56
+ # Only one "mode" will be used; the first of these supplied will take
57
+ # precedence:
58
+ #
59
+ # 1. An already-connected {Sequel::Database} object
60
+ # 2. A connection string
61
+ # 3. A connection options hash
62
+ #
63
+ # While Keycard serves as a singleton, this will raise a DatabaseError
64
+ # if already connected. Check `connected?` if you are unsure.
65
+ #
66
+ # @see {Sequel.connect}
67
+ # @param [Hash] config Optional connection config
68
+ # @option config [String] :url A Sequel database URL
69
+ # @option config [Hash] :opts A set of connection options
70
+ # @option config [Sequel::Database] :db An already-connected database;
71
+ # @return [Sequel::Database] The initialized database connection
72
+ def connect!(config = {})
73
+ raise DatabaseError, ALREADY_CONNECTED if connected?
74
+ merge_config!(config)
75
+ raise DatabaseError, MISSING_CONFIG if self.config.db.nil? && conn_opts.empty?
76
+
77
+ # We splat here because we might give one or two arguments depending
78
+ # on whether we have a string or not; to add our logger regardless.
79
+ @db = self.config.db || Sequel.connect(*conn_opts)
80
+ end
81
+
82
+ # Run any pending migrations.
83
+ # This will connect with the current config if not already conencted.
84
+ def migrate!
85
+ connect! unless connected?
86
+ Sequel.extension :migration
87
+ Sequel::Migrator.run(db, File.join(__dir__, '../../db/migrations'), table: schema_table)
88
+ end
89
+
90
+ def schema_table
91
+ :keycard_schema
92
+ end
93
+
94
+ def schema_file
95
+ 'db/keycard.yml'
96
+ end
97
+
98
+ def dump_schema!
99
+ connect! unless connected?
100
+ version = db[schema_table].first.to_yaml
101
+ File.write(schema_file, SCHEMA_HEADER + version)
102
+ end
103
+
104
+ def load_schema!
105
+ connect! unless connected?
106
+ version = YAML.load_file(schema_file)[:version]
107
+ db[schema_table].delete
108
+ db[schema_table].insert(version: version)
109
+ end
110
+
111
+ def model_files
112
+ []
113
+ end
114
+
115
+ # Merge url, opts, or db settings from a hash into our config
116
+ def merge_config!(config = {})
117
+ self.config.url = config[:url] if config.key?(:url)
118
+ self.config.opts = config[:opts] if config.key?(:opts)
119
+ self.config.db = config[:db] if config.key?(:db)
120
+ end
121
+
122
+ def conn_opts
123
+ log = { logger: Logger.new('db/keycard.log') }
124
+ url = config.url
125
+ opts = config.opts
126
+ if url
127
+ [url, log]
128
+ elsif opts
129
+ [log.merge(opts)]
130
+ else
131
+ []
132
+ end
133
+ end
134
+
135
+ def config
136
+ @config ||= OpenStruct.new(
137
+ url: ENV['KEYCARD_DATABASE_URL'] || ENV['DATABASE_URL']
138
+ )
139
+ end
140
+
141
+ def connected?
142
+ !@db.nil?
143
+ end
144
+
145
+ # The Keycard database
146
+ # @return [Sequel::Database] The connected database; be sure to call initialize! first.
147
+ def db
148
+ raise DatabaseError, CONNECTION_ERROR unless connected?
149
+ @db
150
+ end
151
+
152
+ # Forward the Sequel::Database []-syntax down to db for convenience.
153
+ # Everything else must be called on db directly, but this is nice sugar.
154
+ def [](*args)
155
+ db[*args]
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ipaddr'
4
+
5
+ module Keycard
6
+ # looks up institution ID(s) by IP address
7
+ class InstitutionFinder
8
+ INST_QUERY = <<~SQL
9
+ SELECT inst FROM aa_network WHERE
10
+ ? >= dlpsAddressStart
11
+ AND ? <= dlpsAddressEnd
12
+ AND dlpsAccessSwitch = 'allow'
13
+ AND dlpsDeleted = 'f'
14
+ AND inst is not null
15
+ AND inst NOT IN
16
+ ( SELECT inst FROM aa_network WHERE
17
+ ? >= dlpsAddressStart
18
+ AND ? <= dlpsAddressEnd
19
+ AND dlpsAccessSwitch = 'deny'
20
+ AND dlpsDeleted = 'f' )
21
+ SQL
22
+
23
+ def initialize(db: Keycard::DB.db)
24
+ @db = db
25
+ @stmt = @db[INST_QUERY, *[:$client_ip] * 4].prepare(:select, :unused)
26
+ end
27
+
28
+ def attributes_for(request)
29
+ return {} unless (numeric_ip = numeric_ip(client_ip(request)))
30
+
31
+ insts = insts_for_ip(numeric_ip)
32
+
33
+ if !insts.empty?
34
+ { 'dlpsInstitutionId' => insts }
35
+ else
36
+ {}
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :stmt
43
+
44
+ def insts_for_ip(numeric_ip)
45
+ stmt.call(client_ip: numeric_ip).map { |row| row[:inst] }
46
+ end
47
+
48
+ def numeric_ip(dotted_ip)
49
+ return unless dotted_ip
50
+
51
+ begin
52
+ IPAddr.new(dotted_ip).to_i
53
+ rescue IPAddr::InvalidAddressError
54
+ nil
55
+ end
56
+ end
57
+
58
+ def client_ip(request)
59
+ request.get_header('X-Forwarded-For')
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keycard
4
+ # Railtie to hook Keycard into Rails applications.
5
+ #
6
+ # This does three things at present:
7
+ #
8
+ # 1. Loads our rake tasks, so you can run keycard:migrate from the app.
9
+ # 2. Pulls the Rails database information off of the ActiveRecord
10
+ # connection and puts it on Keycard::DB.config before any application
11
+ # initializers are run.
12
+ # 3. Sets up the Keycard database connection after application
13
+ # initializers have run, if it has not already been done and we are not
14
+ # running as a Rake task. This condition is key because when we are in
15
+ # rails server or console, we want to initialize!, but when we are in
16
+ # a rake task to update the database, we have to let it connect, but
17
+ # not initialize.
18
+ class Railtie < Rails::Railtie
19
+ railtie_name :keycard
20
+
21
+ class << self
22
+ # Register a callback to run before anything in 'config/initializers' runs.
23
+ # The block will get a reference to Keycard::DB.config as its only parameter.
24
+ def before_initializers(&block)
25
+ before_blocks << block
26
+ end
27
+
28
+ # Register a callback to run after anything in 'config/initializers' runs.
29
+ # The block will get a reference to Keycard::DB.config as its only parameter.
30
+ # Keycard::DB.initialize! will not have been automatically called at this
31
+ # point, so this is an opportunity to do so if an initializer has not.
32
+ def after_initializers(&block)
33
+ after_blocks << block
34
+ end
35
+
36
+ # Register a callback to run when Keycard is ready and fully initialized.
37
+ # This will happen once in production, and on each request in development.
38
+ # If you need to do something once in development, you can choose between
39
+ # keeping a flag or using the after_initializers.
40
+ def when_keycard_is_ready(&block)
41
+ ready_blocks << block
42
+ end
43
+
44
+ def before_blocks
45
+ @before ||= []
46
+ end
47
+
48
+ def after_blocks
49
+ @after ||= []
50
+ end
51
+
52
+ def ready_blocks
53
+ @ready ||= []
54
+ end
55
+
56
+ def under_rake!
57
+ @rake = true
58
+ end
59
+
60
+ def under_rake?
61
+ @rake ||= false
62
+ end
63
+ end
64
+
65
+ # This runs before anything in 'config/initializers' runs.
66
+ initializer "keycard.before_initializers", before: :load_config_initializers do
67
+ config = Keycard::DB.config
68
+ unless config.url
69
+ opts = ActiveRecord::Base.connection.instance_variable_get(:@config).dup
70
+ opts.delete(:flags)
71
+ config[:opts] = opts
72
+ end
73
+
74
+ Railtie.before_blocks.each do |block|
75
+ block.call(config.to_h)
76
+ end
77
+ end
78
+
79
+ # This runs after everything in 'config/initializers' runs.
80
+ initializer "keycard.after_initializers", after: :load_config_initializers do
81
+ config = Keycard::DB.config
82
+ Railtie.after_blocks.each do |block|
83
+ block.call(config.to_h)
84
+ end
85
+ end
86
+
87
+ # This runs before any block registered under a `config.to_prepare`, which
88
+ # could be in plugins or initializers that want to use a fully configured
89
+ # Keycard instance. The `to_prepare` hook is run once at the start of a
90
+ # production instance and for every request in development (unless caching
91
+ # is turned on so there is no reloading).
92
+ initializer "keycard.ready", after: :finisher_hook do
93
+ Keycard::DB.initialize! unless Railtie.under_rake?
94
+
95
+ Railtie.ready_blocks.each do |block|
96
+ block.call(Keycard::DB.db)
97
+ end
98
+ end
99
+
100
+ def rake_files
101
+ base = Pathname(__dir__) + '../tasks/'
102
+ [base + 'migrate.rake']
103
+ end
104
+
105
+ rake_tasks do
106
+ Railtie.under_rake!
107
+ rake_files.each { |file| load file }
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keycard
4
+ # This class is responsible for extracting the user attributes (i.e. the
5
+ # complete set of things that determine the user's #identity), given a Rack
6
+ # request.
7
+ class RequestAttributes
8
+ def initialize(request, finder: InstitutionFinder.new)
9
+ @finder = finder
10
+ @request = request
11
+ end
12
+
13
+ def [](attr)
14
+ all[attr]
15
+ end
16
+
17
+ def all
18
+ finder.attributes_for(request)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :finder
24
+ attr_reader :request
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Keycard
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'keycard'
5
+
6
+ if defined?(Rails)
7
+ # When db:schema:dump is called directly, we can tack this on.
8
+ # If we do it unconditionally, db:migrate will try to dump before we have
9
+ # been able to migrate the Keycard tables.
10
+ if Rake.application.top_level_tasks.include?('db:schema:dump')
11
+ Rake::Task['db:schema:dump'].enhance do
12
+ Rake::Task['keycard:schema:dump'].invoke
13
+ end
14
+ end
15
+
16
+ # Run our schema load to make sure that the version number is stored in
17
+ # schema_info, so migrations don't try to double-run. The actual table
18
+ # structure is handled by the Rails schema:dump and schema:load.
19
+ # A db:setup will trigger this, so we don't have to handle it separately.
20
+ Rake::Task['db:schema:load'].enhance do
21
+ Rake::Task['keycard:schema:load'].invoke
22
+ end
23
+
24
+ # We hook into db:migrate for convenience.
25
+ Rake::Task['db:migrate'].enhance do
26
+ Rake::Task['keycard:migrate'].invoke
27
+ end
28
+
29
+ end
30
+
31
+ namespace :keycard do
32
+ desc "Migrate the Keycard database to the latest version"
33
+ task :migrate do
34
+ if defined?(Rails)
35
+ # Load the 'environment', which does the full Rails initialization.
36
+ # The Railtie is smart enough to know whether we are in a Rake task,
37
+ # so it can avoid initializing and we can migrate safely before the
38
+ # models are loaded.
39
+ Rake::Task['environment'].invoke
40
+ end
41
+
42
+ # After migrating, we initialize here, even though it isn't strictly
43
+ # necessary, but it will ensure that migration does a small sanity check
44
+ # that at least all of the tables expected by model classes exist.
45
+ Keycard::DB.migrate!
46
+ Keycard::DB.initialize!
47
+ end
48
+
49
+ # We don't bother defining the schema:dump and schema:load tasks if we're
50
+ # not running under Rails. They exist only to cooperate with the dumps done
51
+ # by Rails, since schema.rb includes any Keycard tables in the same
52
+ # database as the application -- a convenient default mode.
53
+ if defined?(Rails)
54
+ namespace :schema do
55
+ desc "Dump the Keycard version to db/keycard.yml"
56
+ task :dump do
57
+ Rake::Task['environment'].invoke
58
+ Keycard::DB.dump_schema!
59
+ end
60
+
61
+ desc "Load the Keycard version from db/keycard.yml"
62
+ task :load do
63
+ Rake::Task['environment'].invoke
64
+ Keycard::DB.load_schema!
65
+ end
66
+
67
+ # When running under Rails, we dump the schema after migrating so
68
+ # everything stays synced up for db:setup against a new database.
69
+ # Rake::Task['keycard:schema:dump'].invoke
70
+ Rake::Task['keycard:migrate'].enhance do
71
+ Rake::Task['keycard:schema:dump'].invoke
72
+ end
73
+ end
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,235 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keycard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Noah Botimer
8
+ - Aaron Elkiss
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2018-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mysql2
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: sequel
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.16'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.16'
56
+ - !ruby/object:Gem::Dependency
57
+ name: coveralls
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.8'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.8'
70
+ - !ruby/object:Gem::Dependency
71
+ name: pry
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rake
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '10.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '10.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rspec
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '3.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '3.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rubocop
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '0.52'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '0.52'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rubocop-rails
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '1.1'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '1.1'
140
+ - !ruby/object:Gem::Dependency
141
+ name: rubocop-rspec
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '1.16'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '1.16'
154
+ - !ruby/object:Gem::Dependency
155
+ name: sqlite3
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ - !ruby/object:Gem::Dependency
169
+ name: yard
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '0.9'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: '0.9'
182
+ description:
183
+ email:
184
+ - botimer@umich.edu
185
+ - aelkiss@umich.edu
186
+ executables: []
187
+ extensions: []
188
+ extra_rdoc_files: []
189
+ files:
190
+ - ".envrc"
191
+ - ".gitignore"
192
+ - ".rspec"
193
+ - ".rubocop.yml"
194
+ - ".travis.yml"
195
+ - Gemfile
196
+ - LICENSE.md
197
+ - README.md
198
+ - Rakefile
199
+ - bin/console
200
+ - bin/setup
201
+ - db/migrations/1_create_tables.rb
202
+ - keycard.gemspec
203
+ - lib/keycard.rb
204
+ - lib/keycard/db.rb
205
+ - lib/keycard/institution_finder.rb
206
+ - lib/keycard/railtie.rb
207
+ - lib/keycard/request_attributes.rb
208
+ - lib/keycard/version.rb
209
+ - lib/tasks/migrate.rake
210
+ homepage: https://github.com/mlibrary/keycard
211
+ licenses:
212
+ - BSD-3-Clause
213
+ metadata: {}
214
+ post_install_message:
215
+ rdoc_options: []
216
+ require_paths:
217
+ - lib
218
+ required_ruby_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ required_rubygems_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ">="
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ requirements: []
229
+ rubyforge_project:
230
+ rubygems_version: 2.6.13
231
+ signing_key:
232
+ specification_version: 4
233
+ summary: Keycard provides authentication support and user/request information, especially
234
+ in Rails applications.
235
+ test_files: []