keycard 0.1.0

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