diff --git a/Gemfile b/Gemfile index 6e9abb1..b7f5aa3 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,10 @@ gem 'turbolinks' gem 'jbuilder', '~> 2.0' # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', '~> 0.4.0', group: :doc +# Devise gem for authentication +gem 'devise', '~> 3.5.2' +# omniauth-github gem for social signin +gem 'omniauth-github', '~> 1.1.2' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' @@ -53,4 +57,7 @@ group :development, :test do gem 'faker' # Use database_cleaner in place of transactional fixtures gem 'database_cleaner' + # Use pry-rails for debugging + gem 'pry-rails' + gem "pry-byebug" end diff --git a/Gemfile.lock b/Gemfile.lock index 0afe943..33c7d7f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,13 +40,15 @@ GEM autoprefixer-rails (6.0.3) execjs json + bcrypt (3.1.10) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) bootstrap-sass (3.3.5.1) autoprefixer-rails (>= 5.0.0.1) sass (>= 3.3.0) builder (3.2.2) - byebug (6.0.2) + byebug (5.0.0) + columnize (= 0.9.0) capybara (2.5.0) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -61,8 +63,16 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) + columnize (0.9.0) database_cleaner (1.5.0) debug_inspector (0.0.2) + devise (3.5.2) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 3.2.6, < 5) + responders + thread_safe (~> 0.1) + warden (~> 1.2.3) diff-lcs (1.2.5) em-websocket (0.5.1) eventmachine (>= 0.12.9) @@ -77,6 +87,8 @@ GEM railties (>= 3.0.0) faker (1.5.0) i18n (~> 0.5) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) ffi (1.9.10) font-awesome-rails (4.4.0.0) railties (>= 3.2, < 5.0) @@ -101,6 +113,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) + hashie (3.4.2) http_parser.rb (0.6.0) i18n (0.7.0) jbuilder (2.3.2) @@ -111,6 +124,7 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + jwt (1.5.1) listen (3.0.3) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) @@ -124,17 +138,40 @@ GEM mini_portile (0.6.2) minitest (5.8.1) multi_json (1.11.2) + multi_xml (0.5.5) + multipart-post (2.0.0) nenv (0.2.0) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) notiffany (0.0.8) nenv (~> 0.1) shellany (~> 0.0) + oauth2 (1.0.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (~> 1.2) + omniauth (1.2.2) + hashie (>= 1.2, < 4) + rack (~> 1.0) + omniauth-github (1.1.2) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-oauth2 (1.3.1) + oauth2 (~> 1.0) + omniauth (~> 1.2) + orm_adapter (0.5.0) pg (0.18.3) - pry (0.10.2) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) + pry-byebug (3.2.0) + byebug (~> 5.0) + pry (~> 0.10) + pry-rails (0.3.4) + pry (>= 0.9.10) rack (1.6.4) rack-test (0.6.3) rack (>= 1.0) @@ -167,6 +204,8 @@ GEM rb-inotify (0.9.5) ffi (>= 0.5.0) rdoc (4.2.0) + responders (2.1.0) + railties (>= 4.2.0, < 5) rspec (3.3.0) rspec-core (~> 3.3.0) rspec-expectations (~> 3.3.0) @@ -217,6 +256,8 @@ GEM uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) + warden (1.2.3) + rack (>= 1.0) web-console (2.2.1) activemodel (>= 4.0) binding_of_caller (>= 0.7.2) @@ -234,6 +275,7 @@ DEPENDENCIES capybara coffee-rails (~> 4.1.0) database_cleaner + devise (~> 3.5.2) factory_girl_rails faker font-awesome-rails (~> 4.4.0.0) @@ -241,7 +283,10 @@ DEPENDENCIES guard-rspec jbuilder (~> 2.0) jquery-rails + omniauth-github (~> 1.1.2) pg + pry-byebug + pry-rails rails (= 4.2.3) rspec-rails sass-rails (~> 5.0) diff --git a/Guardfile b/Guardfile index 6a0ea2c..fc9b54e 100644 --- a/Guardfile +++ b/Guardfile @@ -26,11 +26,12 @@ guard 'livereload' do watch(%r{app/views/.+\.(erb|haml|slim)$}) + watch(%r{app/controllers/.+\.(rb)$}) watch(%r{app/helpers/.+\.rb}) - watch(%r{public/.+\.(css|js|html)}) + watch(%r{public/.+\.(css|scss|js|html)}) watch(%r{config/locales/.+\.yml}) # Rails Assets Pipeline - watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" } + watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|scss|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" } end guard :rspec, cmd: "bundle exec rspec" do diff --git a/README.md b/README.md index 146dcd1..a5377d3 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,14 @@ _If you *do not* want to use the project default database names, then you'll wan config/database.yml before runnig the last command._ $ rake db:setup + +To be able to use social login with Github, make sure to add your consumer key +and consumer secret in your config/secrets.yml. For example: + +development: + github_consumer_key: 123456........ + github_consumer_secret: 123456......... + +test: + github_consumer_key: 123456........ + github_consumer_secret: 123456......... diff --git a/app/assets/javascripts/achievements.coffee b/app/assets/javascripts/achievements.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/achievements.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/achievements.scss b/app/assets/stylesheets/achievements.scss new file mode 100644 index 0000000..6185af5 --- /dev/null +++ b/app/assets/stylesheets/achievements.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the achievements controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..1efc835 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/achievements_controller.rb b/app/controllers/achievements_controller.rb new file mode 100644 index 0000000..0f316c2 --- /dev/null +++ b/app/controllers/achievements_controller.rb @@ -0,0 +1,49 @@ +class AchievementsController < ApplicationController + before_action :find_achievement, only: [:show, :edit, :update, :destroy] + + def new + @achievement = Achievement.new + end + + def create + @achievement = Achievement.new(achievement_params) + if @achievement.save + redirect_to @achievement + else + render 'new' + end + end + + def show + end + + def index + @achievements = Achievement.all + end + + def edit + end + + def update + if @achievement.update(achievement_params) + redirect_to @achievement + else + render 'edit' + end + end + + def destroy + @achievement.destroy + redirect_to achievements_path + end + + private + + def find_achievement + @achievement = Achievement.find(params[:id]) + end + + def achievement_params + params.require(:achievement).permit(:title, :description, :points) + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..b8915c3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,19 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + before_action :authenticate_user! + before_action :devise_user_params, if: :devise_controller? + + private + + def devise_user_params + devise_parameter_sanitizer.for(:sign_up) { + |user| user.permit(:username, :email, :password, :password_confirmation) + } + + devise_parameter_sanitizer.for(:account_update) { + |user| user.permit(:username, :email, :biography, :password, + :password_confirmation, :current_password) + } + end end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb new file mode 100644 index 0000000..e522d3e --- /dev/null +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -0,0 +1,36 @@ +class OmniauthCallbacksController < Devise::OmniauthCallbacksController + def all + @identity = Identity.find_or_create_from_oauth(auth) + if @identity.user.nil? + create_identity_user_from_oauth + signin_and_redirect_user + elsif @identity.user.persisted? + signin_and_redirect_user + else + session["devise.#{provider}_data"] = request.env["omniauth.auth"] + redirect_to new_user_registration_url + end + end + + alias_method :github, :all + + private + + def auth + @auth ||= request.env["omniauth.auth"] + end + + def provider + @provider ||= auth.provider + end + + def create_identity_user_from_oauth + @identity.user = User.create_from_oauth(auth) + @identity.save + end + + def signin_and_redirect_user + sign_in_and_redirect @identity.user, event: :authentication + set_flash_message(:notice, :success, kind: provider.to_sym) + end +end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index 91da0c2..35cb7ae 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -1,4 +1,6 @@ class StaticPagesController < ApplicationController + skip_before_action :authenticate_user! + def index end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..d7673b0 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,9 @@ +class UsersController < ApplicationController + def show + if params[:id] + @user = User.find(params[:id]) + else + @user = current_user + end + end +end diff --git a/app/helpers/achievements_helper.rb b/app/helpers/achievements_helper.rb new file mode 100644 index 0000000..26cc28c --- /dev/null +++ b/app/helpers/achievements_helper.rb @@ -0,0 +1,2 @@ +module AchievementsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/models/identity.rb b/app/models/identity.rb new file mode 100644 index 0000000..278d255 --- /dev/null +++ b/app/models/identity.rb @@ -0,0 +1,9 @@ +class Identity < ActiveRecord::Base + belongs_to :user + validates :uid, presence: true, uniqueness: { scope: :provider } + validates :provider, presence: true, uniqueness: { scope: :user_id } + + def self.find_or_create_from_oauth(auth) + first_or_create(provider: auth.provider, uid: auth.uid) + end +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..9fc7952 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,15 @@ +class User < ActiveRecord::Base + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :trackable, :validatable, + :omniauthable, omniauth_providers: [:github] + has_many :identities + validates :username, presence: true + + def self.create_from_oauth(auth) + create(username: auth.info.nickname, + email: auth.info.email, + password: Devise.friendly_token[0, 20]) + end +end diff --git a/app/views/achievements/_form.html.erb b/app/views/achievements/_form.html.erb new file mode 100644 index 0000000..b5f12a1 --- /dev/null +++ b/app/views/achievements/_form.html.erb @@ -0,0 +1,21 @@ +<%= form_for @achievement do |f| %> + +
<%= link_to(achievement.title, achievement) %>
+<%= achievement.description %>
+Points: <%= achievement.points %>
+ <% end %> +Points: <%= @achievement.points %>
+Welcome <%= @email %>!
+ +You can confirm your account email through the link below:
+ +<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..f667dc1 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +Hello <%= @resource.email %>!
+ +Someone has requested a link to change your password. You can do this through the link below.
+ +<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
+ +If you didn't request this, please ignore this email.
+Your password won't change until you access the link above and create a new one.
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..41e148b --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +Hello <%= @resource.email %>!
+ +Your account has been locked due to an excessive number of unsuccessful sign in attempts.
+ +Click the link below to unlock your account:
+ +<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..6a796b0 --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,25 @@ +<%= notice %>
+<%= alert %>
+ + <%= yield %> +