From 2c43eff47df66c0ee4b032b265fbb1cdc4eae6e7 Mon Sep 17 00:00:00 2001 From: Sidharth Bansal Date: Thu, 21 Jun 2018 02:32:47 +0530 Subject: [PATCH] Google Authentication (#2771) * Routes modified * Set the views * Modified error messages and user_tag * Add User_Tag as identity and find user_tag * User_sessions_controller modified * User_tag modified * user session controller modified * Checkpoint 1 * checkpoint1 codeclimate errors fixed * Checkpoint 2 * checkpoint 2 * User is created from sign up process successfully * create a usertag_with_omniauth * Search a usertag for oauth * search user_tag existing in db * create a user with omniauth test * LOGIN WORKS * current_user=(user) method removed * sign up correction * routing tests for google oauth * Google auth details saved * Google should return omniauth hash test * sign up and login via provider * sign up and login via provider alternative flow * flash message test added * login user with an email and then connect google provider * . * Changes according to rails 5 done * Changes according to rails 5 done * Environment nomenclature updated * Comments added * Adding application.yml * Submitting a draft (#2747) * Checkbox and JS function added * publish_draft method, routes, mail_notify_stop * coauthor can see and publish * bower update * undo and trying * draft label on dashboard * draft option not visible to first timer * updating bower * debug1 * removing debug statement * button view * view 2 * normal * generate path added * publish privil update * code climate * displaying comment with status1 only * coauthor can view draft * test correction * button text change to save if draft * fixture addition and 1 test * tests , fixtures and minor changes * test addition * codeclimate 1 * codeclimate 2 * codeclimate 3 * minor fixes * enable method=post for data-url image upload at /post (#2675) * enable method=post for data-url image upload at /post * Update editor_controller.rb * Update editor_controller.rb * Fixed first part of Issue #2352 Sorting headers added in wiki index page (#2353) * sorting headers added in wiki index page * replaced sorting header icon * Improved translate widget (#2553) * enhanced translate widget * changed title formatting of widget * Fix the clone command (#2766) * List subscription change (#2767) * Add sidekiq gem (#2738) * added resque gem * made changes for resqu installation * added sidekiq gem * minor changes * modified docker-compose.yml for redis * minor changes * resolve conflicts * Place tag detection and replacing "-" with "" (#2734) * Place tag detection and replacing "-" with "" * Javascript changed * Geocode method added * Tweaks * Tweaks * Tweaks * Tweaks 2 * Tweaks 3 * Update Gemfile.lock * Climate fix * add geocoded tags * Tweaks * Block shift and callback to onComplete * onComplete binded with ajax * place in starting of tag detection * Tweaks * make descriptive tag links move to bottom of page (#2770) * skylight config (#2773) * Reply to comment by email feature (#2669) * Added mailman script * Added migration file for adding comment_via column to comment table * Added reverse_markdown gem for converting html to markdown format * Added migration file for adding message_id column to comment table * Added function to handle received mail to create comment * Added icon for comment added via email to distinguish between the normal comment and reply by email comment * Added node_id in Mailer subjects to be used in reply by email feature * Minor changes * Updated schema.rb.example file * Improved tests * Added migrations * Added migrations * Added migrations * Corrected tests * Added enviroment variable for server address * Added sql sphefic conditions * Unwanted changes in schema.rb.example file is removed * Gem added * Log file is moved to public folder * Corrected tests * forward-date timestamps * Rename 20180605190014_add_message_id_column_to_comments.rb to 20180605010014_add_message_id_column_to_comments.rb * Update schema.rb.example * Added whenever cron update command in Makefile for redeploy-container (#2780) * Fix rss images and email images (#2784) * Fix rss images and email images * Update rss.rss.builder * Update rss_for_tagged_with_author.rss.builder * Update rss.rss.builder * Update rss.rss.builder * Update notify_node_creation.html.erb * Rails 5.0 upgrade (#2726) (#2783) * Rails 5.0 upgrade (#2726) * Upgraded the .gitignore according to rails diff * Initial Gemfile update * Update README.md * Showing message at the top of upgrade page (#2713) * added an upgrade alert tag * Additional questions page tweak (#2721) * added the tag to determine which upgrade is listed on the website * changed regex back * added an upgrade alert tag * added the tag to determine which upgrade is listed on the website * changed regex back * added upgrade after pull and rebase master * Upgrade to Rails 5 * Uncommented composite_primary_key gem * Removed the dual occurence of rails_autolink * Update readme installation, fixes #2729 (#2730) Updates the readme to change the installation instructions, fixes #2729 * Commented activerecord-tableless * Draft display on Dashboard (#2666) * draft display on dashboard * moderator file view permitted * checking * fix * test fix * Added and modified few config files according to railsdiff.org * Removed mass_assignment_sanitizer * Removed composite primary key gem * Removed all attr_accessible in all models * Added composite_primary_key gem again * Downgraded rails and removed few attr_accessible commands * Added strong parameters to the controllers * Added strong parameters gem and also include params.require inside private methods * Added strongparameters.rb file to initializers folder * Removed unnecessary strong_parameter functions * Removed some errors * Changed test command temporarily in .travis.yml * Corrected config/puma.rb file with adding some assets * Added all the config.precompile files in one line * Removed error of problem in updating of user profile * Removed the error while creating a user * Removed error RecordNotFound: Couldn't find Node with 'nid'= * Removed some errors related to ids * Removed comment.reload in comment_controller_test * Track Dockerfiles accross instances. (#2593) * Track Dockerfiles accross instances. * Specify jessie as base * Add commands to build&deploy container with Makefile. (#2746) * Added Sample Cron Job to check working of Whenever Gem (#2715) * Added Sample Cron Job to test Whenever Gem * Added make targets for deployment and cron service start * Added make to image * Tweak Makefile * Fix cron starting command in Makefile * Trim whitespace * Explicitly set jessie as base container * Fixed codeclimate issues * Auto-set wiki slug (#2743) * Added keyword arguments to the tag_model.rb * changed the query statement in search method of node.rb * Force removal of pid file (see https://github.com/docker/compose/issues/1393) * Updated the tests due to change in arguments of search function in node.rb * Removed all the failures from tag_controller_test.rb * Changed the style of params in notes_controller_test.rb * Removed all depreciation warnings related to notes_controller * Removed all deprecation warnings related to image_controller and answers_controller * Removed all deprecation warnings of like_controller and fixed syntax error of answer_controller_test.rb * Removed all dep warnings of editor_controller and syntax error of like_controller_test.rb * removed all errors of answer_controller and some errors of tag_controller.rb * Removed all dep warnings of tag_controller_test.rb * Removed several dep warnings * Removed dep warnings of wiki_controller * Removed dep warnings of talk_controller * Removed dep warnings of legacy_controller_test.rb * Removed some warnings related to comment_controller * Removed the syntax errors * Removed two failures regarding application/xml+rss * Removed 2 failures due to wrong way of passing parameters in tests * Removed 2 failures * Fix params * Remove deprecations * Removed some dep warnings * Remove deprecations * Reverted to nothing => true * Reverted to nothing => true * Avoid rendering more than once in the same action * Removed all dep warnings related to user_tags_controller_test * Remove deprecation * Removed Dep warnings * Fix deprecation * Replace before_filter * Remove deprecation * Remove deprecation * Remove deprecation * Remove deprecation * Bump Ruby version to match Travis' * Simplify email regex to work with Ruby 2.4.1 * Changed the ruby version in Dockerfile * Remove deprecation * Remove deprecation * Removed the depreciation warning related to dynamic :controller and :action segment in routes * Changed the default ActiveRecord to ApplicationRecord in models * Clean Gemfile * Reverted to ['nid'] in tag_controller_test.rb * Changed the tests to rake test:all in .travis.yml * Minor spacing correction in .travis.yml * added a commit in application.js an application.css regarding the precompilation of files * Added more indentation * Small fixes * Separated the rails test and jasmine and coveralls task * Modified ['nid'] to ['node']['nid'] * Removed the extra puts in Rakefile * Changed the serial of tests * Added rake/testtask to Rakefile * Removed puma * Changed to ruby 2.4.1-jessie * Removed the whitelist_attributes command from config/application.rb * Removed two failures due to old usage of params * Fixed some new deprecation warnigs introduced * fixes #2731 'add spam button,line 16 _answers.html.erb' (#2785) * add spam button,line 16 _answers.html.erb * changed comment.ids to answer.ids * changed from comment path to answer * changed comment.parent.path to answer.path * some more cleanup to the button * change answer.aid to answer id * First timers recaptcha Environment Variables (#2786) * Edited the if statement * Added the env variables * Edited ENV variable, and added fallback * Fixed issue#2445 added both direction sort order (#2557) * fixed issue#2445 added both direction sort order * added function get_order_string in tags_controller * Changed get to post (#2788) * Update container definitions to match actual files used. (#2791) * Update container definitions to match actual files used. * Fix paths for running docker-compose.yml from subdir * Fix reference to Dockerfile * Reformat range stats page to table (#2792) * Index page listing for UserTags per issue #2741. (#2753) * Index page listing for UserTags per issue #2741. * Changes per pull request checks. * Removed debugging line. * Added routes to reflect 'groups' naming convention for user_tags per pull request spec. * Tests rewritten after rebase. * Index page listing for UserTags per issue #2741. * Changes per pull request checks. * Removed debugging line. * Added routes to reflect 'groups' naming convention for user_tags per pull request spec. * Fix for test of sort by value. * Update routes.rb for wiki update (#2804) * Update routes.rb * fixes * integration tests for assets (#2806) * integration tests for assets * full assets included * Update assets.rb * Added routes to notes/edit (#2808) * Added routes to notes/edit * Removed redundant notes#edit and added get instead of post in edit actions * Update routes.rb (#2810) * Create OPENID.md * fixing error in home_controller related to `group by` (#2794) * Added group by note.nid * correction * Openid fix with post route and better alert texts (#2815) * Added group by note.nid * correction * reworded openid requests and added post method for 2nd step * adjust message * Update openid_test.rb * Update openid_test.rb * Update openid_test.rb * Update openid_test.rb * Update Dangerfile (#2816) * Update routes.rb to fix embeddable features (#2818) * Update routes.rb * Removed the apostrophe mismatch * Added tests for embed in feature * Delete route for notes (#2820) * Documentation for the reply-by-email to comment feature (#2774) * Added documentation for the reply-by-email to comment feature * Added documentation for the reply-by-email to comment feature * Added documentation for the reply-by-email to comment feature * Added documentation for the reply-by-email to comment feature * Re-order comment options and messages (#2834) * Update _comment.html.erb * Update _comment.html.erb * encoding issue resolved for migrating DrupalUsers to User records (#2257) * encoding issue solved fixes #2209 * minor changes * Rename 20180206161706_add_user_for_orphaned_drupal_user.rb to 20180614000000_add_user_for_orphaned_drupal_user.rb * resolves issue 2520 (#2831) * Emoji autocomplete (#2832) * basic emoji autocomplete * minor changes * minor tweaks * code climate fixes * minor change * changes * remove unnecessary style * Fix parent powertags for wikis like /barnraising (#2844) * application.yml added * Views removed * Application.yml removed * application removed * Docker changes * sign up page removed --- .gitignore | 2 +- app/controllers/application_controller.rb | 5 + app/controllers/user_sessions_controller.rb | 179 ++++++++++++------ app/controllers/user_tags_controller.rb | 8 +- app/controllers/users_controller.rb | 2 +- app/models/node.rb | 8 +- app/models/user.rb | 18 ++ app/models/user_tag.rb | 11 +- app/views/layouts/_header.html.erb | 6 +- app/views/user_sessions/new.html.erb | 6 +- app/views/users/edit.html.erb | 2 +- config/environments/test.rb | 20 ++ config/initializers/omniauth.rb | 6 +- config/routes.rb | 3 +- containers/docker-compose-production.yml | 2 + containers/docker-compose-stable.yml | 2 + containers/docker-compose-unstable.yml | 2 + test/fixtures/user_tags.yml | 40 ++++ .../user_sessions_controller_test.rb | 54 ++++++ test/integration/login_flow_test.rb | 17 ++ test/unit/user_tag_test.rb | 109 +++++++++++ test/unit/user_test.rb | 21 ++ 22 files changed, 442 insertions(+), 81 deletions(-) diff --git a/.gitignore b/.gitignore index f9efa92dac..503b225147 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,7 @@ production.db test.db solr/* test/reports/ - +!config/application.yml # Ignore NPM node_modules .byebug_history diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7a8e2a612d..547d905abf 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -186,4 +186,9 @@ def redirect_old_urls redirect_to @node.path, status: :moved_permanently end end + + def signed_in? + !!current_user + end + end diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user_sessions_controller.rb index fc1ca35d64..a47c957a43 100644 --- a/app/controllers/user_sessions_controller.rb +++ b/app/controllers/user_sessions_controller.rb @@ -6,75 +6,132 @@ def new end def create - params[:user_session][:username] = params[:openid] if params[:openid] # second runthrough must preserve username - username = params[:user_session][:username] if params[:user_session] - @user = User.find_by(username: username) + auth = request.env['omniauth.auth'] + if auth + # Find an identity here + @identity = UserTag.find_with_omniauth(auth) - # try finding by email, if that exists - if @user.nil? && !User.where(email: username).empty? - @user = User.find_by(email: username) - params[:user_session][:username] = @user.username - end - - if @user.nil? - flash[:warning] = "There is nobody in our system by that name, are you sure you have the right username?" - redirect_to '/login' - elsif params[:user_session].nil? || @user&.drupal_user&.status == 1 - # an existing Rails user - if params[:user_session].nil? || @user - if @user&.crypted_password.nil? # the user has not created a pwd in the new site - params[:user_session][:openid_identifier] = 'https://old.publiclab.org/people/' + username + '/identity' if username - params[:user_session].delete(:password) - params[:user_session].delete(:username) - params[:openid] = username # pack up username for second runthrough + if signed_in? + if @identity.nil? + # If no identity was found, create a brand new one here + @identity = UserTag.create_with_omniauth(auth, current_user.id) + # The identity is not associated with the current_user so lets + # associate the identity + @identity.user = current_user + @identity.save + redirect_to root_url, notice: "Successfully linked to your account!" + elsif @identity.user == current_user + # User is signed in so they are trying to link an identity with their + # account. But we found the identity and the user associated with it + # is the current user. So the identity is already associated with + # this user. So let's display an error message. + redirect_to root_url, notice: "Already linked to your account!" + else + # User is signed in so they are trying to link an identity with their + # account. But we found the identity and a different user associated with it + # ,which is not the current user. So the identity is already associated with + # that user. So let's display an error message. + redirect_to root_url, notice: "Already linked to another account!" end - @user_session = UserSession.new(username: params[:user_session][:username], - password: params[:user_session][:password], - remember_me: params[:user_session][:remember_me]) - saved = @user_session.save do |result| - if result - # replace this with temporarily saving pwd in session, - # and automatically saving it in the user record after login is completed - if current_user.crypted_password.nil? # the user has not created a pwd in the new site - flash[:warning] = I18n.t('user_sessions_controller.create_password_for_new_site') - redirect_to '/profile/edit' - else - flash[:notice] = I18n.t('user_sessions_controller.logged_in') - if session[:openid_return_to] # for openid login, redirects back to openid auth process - return_to = session[:openid_return_to] - session[:openid_return_to] = nil - redirect_to return_to - elsif session[:return_to] - return_to = session[:return_to] - session[:return_to] = nil - redirect_to return_to - elsif params[:return_to] - redirect_to params[:return_to] + else # not signed in + if @identity&.user.present? + # The identity we found had a user associated with it so let's + # just log them in here + UserSession.create( @identity.user) + redirect_to root_url, notice: "Signed in!" + else #identity does not exist so we need to either create a user with identity OR link identity to existing user + if User.where(email: auth["info"]["email"]).empty? + #Create a new user as email provided is not present in PL database + user = User.create_with_omniauth(auth) + @identity = UserTag.create_with_omniauth(auth, user.id) + key = user.generate_reset_key + # send key to user email + PasswordResetMailer.reset_notify(user, key).deliver_now unless user.nil? # respond the same to both successes and failures; security + redirect_to root_url, notice: "You have successfully signed in. Please change your password via a link sent to you via a mail" + else #email exists so link the identity with existing user and log in the user + user = User.where(email: auth["info"]["email"]) + # If no identity was found, create a brand new one here + @identity = UserTag.create_with_omniauth(auth, user.ids.first) + # The identity is not associated with the current_user so lets + # associate the identity + @identity.save + #log in them + UserSession.create( @identity.user) + redirect_to root_url, notice: "Successfully linked to your account!" + end + end + end + else + params[:user_session][:username] = params[:openid] if params[:openid] # second runthrough must preserve username + username = params[:user_session][:username] if params[:user_session] + @user = User.find_by(username: username) + + # try finding by email, if that exists + if @user.nil? && !User.where(email: username).empty? + @user = User.find_by(email: username) + params[:user_session][:username] = @user.username + end + + if @user.nil? + flash[:warning] = "There is nobody in our system by that name, are you sure you have the right username?" + redirect_to '/login' + elsif params[:user_session].nil? || @user&.drupal_user&.status == 1 + # an existing Rails user + if params[:user_session].nil? || @user + if @user&.crypted_password.nil? # the user has not created a pwd in the new site + params[:user_session][:openid_identifier] = 'https://old.publiclab.org/people/' + username + '/identity' if username + params[:user_session].delete(:password) + params[:user_session].delete(:username) + params[:openid] = username # pack up username for second runthrough + end + @user_session = UserSession.new(username: params[:user_session][:username], + password: params[:user_session][:password], + remember_me: params[:user_session][:remember_me]) + saved = @user_session.save do |result| + if result + # replace this with temporarily saving pwd in session, + # and automatically saving it in the user record after login is completed + if current_user.crypted_password.nil? # the user has not created a pwd in the new site + flash[:warning] = I18n.t('user_sessions_controller.create_password_for_new_site') + redirect_to '/profile/edit' else - redirect_to '/dashboard' + flash[:notice] = I18n.t('user_sessions_controller.logged_in') + if session[:openid_return_to] # for openid login, redirects back to openid auth process + return_to = session[:openid_return_to] + session[:openid_return_to] = nil + redirect_to return_to + elsif session[:return_to] + return_to = session[:return_to] + session[:return_to] = nil + redirect_to return_to + elsif params[:return_to] + redirect_to params[:return_to] + else + redirect_to '/dashboard' + end end + else + # Login failed; probably bad password. + # Errors will display on login form: + render action: 'new' end - else - # Login failed; probably bad password. - # Errors will display on login form: - render action: 'new' + end + else # not a native user + if !DrupalUser.find_by(name: username).nil? + # this is a user from the old site who hasn't registered on the new site + redirect_to controller: :users, action: :create, user: { openid_identifier: username } + else # totally new user! + flash[:warning] = I18n.t('user_sessions_controller.sign_up_to_join') + redirect_to '/signup' end end - else # not a native user - if !DrupalUser.find_by(name: username).nil? - # this is a user from the old site who hasn't registered on the new site - redirect_to controller: :users, action: :create, user: { openid_identifier: username } - else # totally new user! - flash[:warning] = I18n.t('user_sessions_controller.sign_up_to_join') - redirect_to '/signup' - end + elsif params[:user_session].nil? || @user&.drupal_user&.status == 5 + flash[:error] = I18n.t('user_sessions_controller.user_has_been_moderated', username: @user.username).html_safe + redirect_to '/' + else + flash[:error] = I18n.t('user_sessions_controller.user_has_been_banned', username: @user.username).html_safe + redirect_to '/' end - elsif params[:user_session].nil? || @user&.drupal_user&.status == 5 - flash[:error] = I18n.t('user_sessions_controller.user_has_been_moderated', username: @user.username).html_safe - redirect_to '/' - else - flash[:error] = I18n.t('user_sessions_controller.user_has_been_banned', username: @user.username).html_safe - redirect_to '/' end end diff --git a/app/controllers/user_tags_controller.rb b/app/controllers/user_tags_controller.rb index 92130e2e79..40b9282c55 100644 --- a/app/controllers/user_tags_controller.rb +++ b/app/controllers/user_tags_controller.rb @@ -54,13 +54,13 @@ def create unless exist user_tag = user.user_tags.build(value: name) - if tagname.split(':')[0] == "oauth-facebook" + if tagname.split(':')[1] == "facebook" @output[:errors] << "This tag is used for associating a Facebook account. Click here to read more " - elsif tagname.split(':')[0] == "oauth-github" + elsif tagname.split(':')[1] == "github" @output[:errors] << "This tag is used for associating a Github account. Click here to read more " - elsif tagname.split(':')[0] == "oauth-google" + elsif tagname.split(':')[1] == "google_oauth2" @output[:errors] << "This tag is used for associating a Google account. Click here to read more " - elsif tagname.split(':')[0] == "oauth-twitter" + elsif tagname.split(':')[1] == "twitter" @output[:errors] << "This tag is used for associating a Twitter account. Click here to read more " elsif user_tag.save @output[:saved] << [name, user_tag.id] diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d4171b313d..d7cf5aaff2 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -98,7 +98,7 @@ def list .where('rusers.role = ?', params[:id]) .where('rusers.status = 1') .page(params[:page]) - + elsif @tagname_param @users = User.where(id: UserTag.where(value: @tagname_param).collect(&:uid)) .page(params[:page]) diff --git a/app/models/node.rb b/app/models/node.rb index 98b2a9af81..3c14fe08ac 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -861,13 +861,13 @@ def can_tag(tagname, user, errors = false) errors ? I18n.t('node.only_admins_can_lock') : false elsif tagname.split(':')[0] == 'redirect' && Node.where(slug: tagname.split(':')[1]).length <= 0 errors ? I18n.t('node.page_does_not_exist') : false - elsif tagname.split(':')[0] == "oauth-facebook" + elsif tagname.split(':')[1] == "facebook" errors ? "This tag is used for associating a Facebook account. Click here to read more " : false - elsif tagname.split(':')[0] == "oauth-github" + elsif tagname.split(':')[1] == "github" errors ? "This tag is used for associating a Github account. Click here to read more " : false - elsif tagname.split(':')[0] == "oauth-google" + elsif tagname.split(':')[1] == "google_oauth2" errors ? "This tag is used for associating a Google account. Click here to read more " : false - elsif tagname.split(':')[0] == "oauth-twitter" + elsif tagname.split(':')[1] == "twitter" errors ? "This tag is used for associating a Twitter account. Click here to read more " : false else true diff --git a/app/models/user.rb b/app/models/user.rb index 01552a668f..617ee8f5e2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -414,4 +414,22 @@ def self.contributor_count_for(start_time,end_time) contributors end + def self.create_with_omniauth(auth) + #email prefix is part of email before @ with periods replaced with underscores + #generate a 2 digit alphanumeric number and append it at the end of email-prefix + charset = Array('A'..'Z') + Array('a'..'z') + Array(0..9) + email_prefix = auth["info"]["email"].gsub('.','_').split('@')[0] + while(!User.where(username: email_prefix).empty?) + email_prefix = auth["info"]["email"].gsub('.','_').split('@')[0] + Array.new(2) { charset.sample }.join + end + puts(auth) + create! do |user| + user.username = email_prefix + user.email = auth["info"]["email"] + user.password = auth["uid"] + user.password_confirmation = auth["uid"] + user.save! + end + end + end diff --git a/app/models/user_tag.rb b/app/models/user_tag.rb index ef429f010e..9b95ff872c 100644 --- a/app/models/user_tag.rb +++ b/app/models/user_tag.rb @@ -3,7 +3,7 @@ class UserTag < ApplicationRecord validates :value, presence: :true validates :value, format: { with: /\A[\w\.:-]*\z/, message: 'can only include letters, numbers, and dashes' } - + validates_uniqueness_of :value, :scope => :uid before_save :preprocess def preprocess @@ -18,4 +18,13 @@ def name self.value end + def self.find_with_omniauth(auth) + find_by(value: "oauth:" + auth['provider'] + ":" + auth['uid']) + end + + def self.create_with_omniauth(auth, uid) + create(value: "oauth:" + auth['provider'] + ":" + auth['uid'], + uid: uid) + end + end diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index af7133774d..95d8074467 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -48,13 +48,13 @@