From dd3f96fdb61cd103fa88f15a2670114fde7eea3f Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Fri, 17 Mar 2017 16:20:40 -0400 Subject: [PATCH] Set up TravisCI, rubocop, reek (#7) **Why**: Help us maintain high-quality code --- .reek | 21 +++++ .rubocop.yml | 223 ++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 12 +++ Gemfile | 15 ++- Gemfile.lock | 42 ++++++++- Makefile | 6 +- README.md | 5 + app.rb | 31 +++--- spec/app_spec.rb | 6 ++ spec/spec_helper.rb | 21 +++++ 10 files changed, 360 insertions(+), 22 deletions(-) create mode 100644 .reek create mode 100644 .rubocop.yml create mode 100644 .travis.yml create mode 100644 spec/app_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.reek b/.reek new file mode 100644 index 0000000..0c19299 --- /dev/null +++ b/.reek @@ -0,0 +1,21 @@ +Attribute: + enabled: false +IrresponsibleModule: + enabled: false +TooManyStatements: + max_statements: 6 +UtilityFunction: + public_methods_only: true +'spec': + FeatureEnvy: + enabled: false + TooManyInstanceVariables: + enabled: false + TooManyMethods: + enabled: false + TooManyStatements: + enabled: false + UtilityFunction: + enabled: false +exclude_paths: + - db/migrate diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..8fe94f7 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,223 @@ +# This configuration only includes the cops that differ from the Rubocop +# defaults, which can be found here: +# https://github.com/bbatsov/rubocop/blob/master/config/default.yml +# https://github.com/bbatsov/rubocop/blob/master/config/enabled.yml +# https://github.com/bbatsov/rubocop/blob/master/config/disabled.yml + +AllCops: + Include: + - '**/Gemfile' + - '**/Rakefile' + TargetRubyVersion: 2.3 + UseCache: true + +Rails: + Enabled: true + +Metrics/AbcSize: + Description: A calculated magnitude based on number of assignments, branches, and + conditions. + Enabled: true + Max: 15 + Exclude: + - spec/**/* + +Metrics/BlockLength: + CountComments: false # count full line comments? + Enabled: true + Max: 25 + Exclude: + - 'spec/**/*.rb' + +Metrics/ClassLength: + Description: Avoid classes longer than 100 lines of code. + Enabled: true + CountComments: false + Max: 100 + Exclude: + - spec/**/* + +Metrics/LineLength: + Description: Limit lines to 100 characters. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits + Enabled: true + Max: 100 + AllowURI: true + URISchemes: + - http + - https + Exclude: + - 'config/routes.rb' + +Metrics/MethodLength: + Description: Avoid methods longer than 10 lines of code. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods + Enabled: true + CountComments: false + Max: 10 + Exclude: + - spec/**/* + +Metrics/ModuleLength: + CountComments: false + Max: 100 + Description: Avoid modules longer than 100 lines of code. + Enabled: true + Exclude: + - spec/**/* + +# This is a Rails 5 feature, so it should be disabled until we upgrade +Rails/HttpPositionalArguments: + Description: 'Use keyword arguments instead of positional arguments in http method calls.' + Enabled: false + Include: + - 'spec/**/*' + - 'test/**/*' + +Rails/OutputSafety: + Exclude: + - lib/i18n_override.rb + +Style/AlignParameters: + # Alignment of parameters in multi-line method calls. + # + # The `with_first_parameter` style aligns the following lines along the same + # column as the first parameter. + # + # method_call(a, + # b) + # + # The `with_fixed_indentation` style aligns the following lines with one + # level of indentation relative to the start of the line with the method call. + # + # method_call(a, + # b) + Description: >- + Align the parameters of a method call if they span more + than one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' + EnforcedStyle: with_first_parameter + SupportedStyles: + - with_first_parameter + - with_fixed_indentation + # By default, the indentation width from Style/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Style/AndOr: + Description: Use &&/|| instead of and/or. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-and-or-or + EnforcedStyle: conditionals + SupportedStyles: + - always + - conditionals + +Style/Documentation: + Description: Document classes and non-namespace modules. + Enabled: false + Exclude: + - 'spec/**/*' + +Style/DotPosition: + Description: Checks the position of the dot in multi-line method calls. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains + EnforcedStyle: trailing + SupportedStyles: + - leading + - trailing + +# Warn on empty else statements +# empty - warn only on empty else +# nil - warn on else with nil in it +# both - warn on empty else and else with nil in it +Style/EmptyElse: + EnforcedStyle: both + SupportedStyles: + - empty + - nil + - both + +Style/ExtraSpacing: + # When true, allows most uses of extra spacing if the intent is to align + # things with the previous or next line, not counting empty lines or comment + # lines. + AllowForAlignment: true + # When true, forces the alignment of = in assignments on consecutive lines. + ForceEqualSignAlignment: false + +Style/FrozenStringLiteralComment: + Description: >- + Add the frozen_string_literal comment to the top of files + to help transition from Ruby 2.3.0 to Ruby 3.0. + Enabled: false + +Style/IfUnlessModifier: + Description: Favor modifier if/unless usage when you have a single-line body. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier + Enabled: true + MaxLineLength: 80 + +# Checks the indentation of the first element in an array literal. +Style/IndentArray: + # The value `special_inside_parentheses` means that array literals with + # brackets that have their opening bracket on the same line as a surrounding + # opening round parenthesis, shall have their first element indented relative + # to the first position inside the parenthesis. + # + # The value `consistent` means that the indentation of the first element shall + # always be relative to the first position of the line where the opening + # bracket is. + # + # The value `align_brackets` means that the indentation of the first element + # shall always be relative to the position of the opening bracket. + EnforcedStyle: special_inside_parentheses + SupportedStyles: + - special_inside_parentheses + - consistent + - align_brackets + # By default, the indentation width from Style/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Style/MultilineOperationIndentation: + EnforcedStyle: aligned + SupportedStyles: + - aligned + - indented + # By default, the indentation width from Style/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Style/StringLiterals: + Description: Checks if uses of quotes match the configured preference. + StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals + EnforcedStyle: single_quotes + SupportedStyles: + - single_quotes + - double_quotes + ConsistentQuotesInMultiline: true + +Style/TrailingCommaInArguments: + # If `comma`, the cop requires a comma after the last argument, but only for + # parenthesized method calls where each argument is on its own line. + # If `consistent_comma`, the cop requires a comma after the last argument, + # for all parenthesized method calls with arguments. + EnforcedStyleForMultiline: no_comma + SupportedStyles: + - comma + - consistent_comma + - no_comma + +Style/TrailingCommaInLiteral: + # If `comma`, the cop requires a comma after the last item in an array or + # hash, but only when each item is on its own line. + # If `consistent_comma`, the cop requires a comma after the last item of all + # non-empty array and hash literals. + EnforcedStyleForMultiline: comma + SupportedStyles: + - comma + - consistent_comma + - no_comma + +Style/SingleLineBlockParams: + Enabled: false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..de5d608 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +bundler_args: "--deployment --jobs=3 --retry=3 --without deploy development doc production" +cache: bundler +language: ruby +matrix: + fast_finish: true +notifications: + email: false +rvm: + - ruby-2.3.3 +sudo: false +script: + - make test diff --git a/Gemfile b/Gemfile index 63a7f47..199bcc8 100644 --- a/Gemfile +++ b/Gemfile @@ -3,15 +3,22 @@ source 'https://rubygems.org' ruby '2.3.3' -gem 'sinatra' -gem 'http' gem 'activesupport' +gem 'dotenv' +gem 'http' gem 'json-jwt' gem 'jwt' -gem 'dotenv' +gem 'sinatra' group :development do + gem 'pry-byebug' gem 'reek' gem 'rubocop', require: false - gem 'pry' +end + +group :test do + gem 'nokogiri' + gem 'rack-test' + gem 'rspec', '~> 3.5.0' + gem 'webmock' end diff --git a/Gemfile.lock b/Gemfile.lock index e6536ff..c77ff0c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,10 +1,10 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.8) + activesupport (5.0.2) + concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) addressable (2.5.0) public_suffix (~> 2.0, >= 2.0.2) @@ -14,17 +14,23 @@ GEM ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) bindata (2.3.5) + byebug (9.0.6) codeclimate-engine-rb (0.4.0) virtus (~> 1.0) coderay (1.1.1) coercible (1.0.0) descendants_tracker (~> 0.0.1) + concurrent-ruby (1.0.5) + crack (0.4.3) + safe_yaml (~> 1.0.0) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) + diff-lcs (1.3) domain_name (0.5.20170223) unf (>= 0.0.5, < 1.0.0) dotenv (2.2.0) equalizer (0.0.11) + hashdiff (0.3.2) http (2.2.1) addressable (~> 2.3) http-cookie (~> 1.0) @@ -44,8 +50,11 @@ GEM url_safe_base64 jwt (1.5.6) method_source (0.8.2) + mini_portile2 (2.1.0) minitest (5.10.1) multi_json (1.12.1) + nokogiri (1.7.0.1) + mini_portile2 (~> 2.1.0) parser (2.4.0.0) ast (~> 2.2) powerpack (0.1.1) @@ -53,15 +62,33 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) + pry-byebug (3.4.2) + byebug (~> 9.0) + pry (~> 0.10) public_suffix (2.0.5) rack (1.6.5) rack-protection (1.5.3) rack + rack-test (0.6.3) + rack (>= 1.0) rainbow (2.2.1) reek (4.5.6) codeclimate-engine-rb (~> 0.4.0) parser (>= 2.3.1.2, < 2.5) rainbow (~> 2.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) rubocop (0.47.1) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) @@ -69,6 +96,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) + safe_yaml (1.0.4) securecompare (1.0.0) sinatra (1.4.8) rack (~> 1.5) @@ -89,6 +117,10 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) + webmock (2.3.2) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff PLATFORMS ruby @@ -99,10 +131,14 @@ DEPENDENCIES http json-jwt jwt - pry + nokogiri + pry-byebug + rack-test reek + rspec (~> 3.5.0) rubocop sinatra + webmock RUBY VERSION ruby 2.3.3p222 diff --git a/Makefile b/Makefile index 02c2168..e588ce0 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,11 @@ PORT ?= 9292 all: check setup: - bundle install + bundle check || bundle install [ -f .env ] && echo ".env exists" || cat .env.example >> .env +.env: setup + check: lint test lint: @@ -23,3 +25,5 @@ lint: run: bundle exec rackup -p $(PORT) +test: .env $(CONFIG) + bundle exec rspec diff --git a/README.md b/README.md index c9cb762..6c0cefe 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,11 @@ These instructions assume [`identity-idp`](https://github.com/18F/identity-idp) $ make run ``` +3. To run specs: + + ``` + $ make test + ``` ## Contributing diff --git a/app.rb b/app.rb index 46bbf45..b30905b 100644 --- a/app.rb +++ b/app.rb @@ -10,7 +10,6 @@ require 'securerandom' require 'sinatra/base' require 'time' -require 'pry' class OpenidConnectRelyingParty < Sinatra::Base SERVICE_PROVIDER = ENV['IDP_SP_URL'] @@ -25,7 +24,7 @@ class OpenidConnectRelyingParty < Sinatra::Base scope: 'openid email', redirect_uri: ENV['REDIRECT_URI'], state: SecureRandom.urlsafe_base64, - prompt: 'select_account' + prompt: 'select_account', }.to_query erb :index, locals: { authorization_url: authorization_url } @@ -47,29 +46,33 @@ def openid_configuration end def token(code) - jwt_payload = { - iss: CLIENT_ID, - sub: CLIENT_ID, - aud: openid_configuration[:token_endpoint], - jti: SecureRandom.urlsafe_base64, - exp: Time.now.to_i + 1000 - } - - jwt = JWT.encode(jwt_payload, sp_private_key, 'RS256') - json HTTP.post( openid_configuration[:token_endpoint], json: { grant_type: 'authorization_code', code: code, client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', - client_assertion: jwt + client_assertion: client_assertion_jwt, } ) end + def client_assertion_jwt + jwt_payload = { + iss: CLIENT_ID, + sub: CLIENT_ID, + aud: openid_configuration[:token_endpoint], + jti: SecureRandom.urlsafe_base64, + exp: Time.now.to_i + 1000, + } + + JWT.encode(jwt_payload, sp_private_key, 'RS256') + end + def userinfo(id_token) - JWT.decode(id_token, idp_public_key, true, algorithm: 'RS256', leeway: 5).first.with_indifferent_access + JWT.decode(id_token, idp_public_key, true, algorithm: 'RS256', leeway: 5). + first. + with_indifferent_access end def json(response) diff --git a/spec/app_spec.rb b/spec/app_spec.rb new file mode 100644 index 0000000..56f32af --- /dev/null +++ b/spec/app_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require 'nokogiri' +require 'securerandom' + +RSpec.describe OpenidConnectRelyingParty do +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..736a007 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,21 @@ +require 'rack/test' +require 'rspec' +require 'webmock/rspec' + +ENV['RACK_ENV'] = 'test' + +$LOAD_PATH.unshift File.expand_path('../..', __FILE__) +require 'app' + +module RSpecMixin + include Rack::Test::Methods + + def app + described_class + end +end + +RSpec.configure do |config| + config.include RSpecMixin + config.disable_monkey_patching! +end