Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api auth #1

Merged
merged 2 commits into from May 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rspec
@@ -0,0 +1,2 @@
--color
--require spec_helper
8 changes: 5 additions & 3 deletions Gemfile
Expand Up @@ -7,9 +7,7 @@ gem 'uglifier'
gem 'sass-rails'
gem 'therubyracer'

# Authentication
gem 'devise'
gem 'devise-encryptable'
gem 'bcrypt'

# Better output layout in console
gem 'hirb'
Expand Down Expand Up @@ -40,4 +38,8 @@ group :development, :test do
gem 'capistrano-rails'
gem 'capistrano-rvm'
gem 'capistrano-passenger'

# Tests
gem 'rspec-rails', '~> 3.0'
gem "factory_girl_rails", "~> 4.0"
end
42 changes: 26 additions & 16 deletions Gemfile.lock
Expand Up @@ -64,22 +64,19 @@ GEM
sshkit (~> 1.2)
colorize (0.7.7)
daemons (1.2.2)
devise (3.4.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
responders
thread_safe (~> 0.1)
warden (~> 1.2.3)
devise-encryptable (0.2.0)
devise (>= 2.1.0)
diff-lcs (1.2.5)
erubis (2.7.0)
eventmachine (1.0.7)
execjs (2.5.2)
exiftool (0.6.0)
json
exiftool_vendored (9.78.0)
exiftool (>= 0.6.0)
factory_girl (4.2.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.2.1)
factory_girl (~> 4.2.0)
railties (>= 3.0.0)
globalid (0.3.5)
activesupport (>= 4.1.0)
hirb (0.7.3)
Expand Down Expand Up @@ -110,7 +107,6 @@ GEM
net-ssh (2.9.2)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
orm_adapter (0.5.0)
rack (1.6.0)
rack-test (0.6.3)
rack (>= 1.0)
Expand Down Expand Up @@ -142,9 +138,24 @@ GEM
rake (10.4.2)
rdiscount (2.1.8)
ref (1.0.5)
responders (2.1.0)
railties (>= 4.2.0, < 5)
rmagick (2.14.0)
rspec-core (3.2.3)
rspec-support (~> 3.2.0)
rspec-expectations (3.2.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.2.0)
rspec-mocks (3.2.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.2.0)
rspec-rails (3.2.1)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.2.0)
rspec-expectations (~> 3.2.0)
rspec-mocks (~> 3.2.0)
rspec-support (~> 3.2.0)
rspec-support (3.2.2)
sass (3.4.13)
sass-rails (5.0.3)
railties (>= 4.0.0, < 5.0)
Expand Down Expand Up @@ -182,22 +193,20 @@ GEM
kgio (~> 2.6)
rack
raindrops (~> 0.7)
warden (1.2.3)
rack (>= 1.0)

PLATFORMS
ruby

DEPENDENCIES
aws-sdk
bcrypt
capistrano (~> 3.4.0)
capistrano-bundler
capistrano-passenger
capistrano-rails
capistrano-rvm
devise
devise-encryptable
exiftool_vendored
factory_girl_rails (~> 4.0)
hirb
jbuilder (~> 2.0)
mini_exiftool_vendored
Expand All @@ -206,6 +215,7 @@ DEPENDENCIES
rails (= 4.2.1)
rdiscount
rmagick
rspec-rails (~> 3.0)
sass-rails
spring
therubyracer
Expand Down
143 changes: 143 additions & 0 deletions app/controllers/api/v1/auth_controller.rb
@@ -0,0 +1,143 @@
class Api::V1::AuthController < Api::V1::BaseController
before_action :authenticate!, except: [ :register, :login, :validate, :reset_password_instructions, :reset_password, :unlock_instructions, :unlock, :confirmation_instructions, :confirm ]
before_action :set_user, only: [ :register, :login, :reset_password_instructions, :unlock_instructions, :confirmation_instructions ]
before_action :set_user_from_request, only: [ :reset_password, :unlock, :confirm ]

def register
if @user
if !@user.confirmed?
render_message({ notice: scoped_t('auth.registered_and_unconfirmed'), status: :unauthorized })
else
render_message({ notice: scoped_t('auth.registered_already'), location: login_api_auth_index_path, status: :unauthorized })
end
else
@user = User.create(user_params)
if @user.valid?
render_message({ user: @user, notice: scoped_t('auth.registered_successfully') })
else
render_message({ error: @user.errors.full_messages, notice: scoped_t('auth.registration_failed'), status: :unprocessable_entity })
end
end
end

def login
if @user
if @user.locked?
render_message({ notice: scoped_t('auth.user_locked'), location: unlock_api_auth_index_path, status: :unauthorized })
elsif !@user.confirmed?
render_message({ notice: scoped_t('auth.user_not_confirmed'), location: login_api_auth_index_path, status: :unauthorized })
elsif @user.valid_password?(user_params[:password])
render_message({ user: @user, token: @user.issue_token, notice: scoped_t('auth.login_success') })
else
render_message({ notice: scoped_t('auth.invalid_credentials'), status: :unauthorized })
end
else
render_message({ notice: scoped_t('auth.invalid_credentials'), status: :unauthorized })
end
end

def user
render_message({ user: @current_user })
end

def logout
if @current_user.destroy_token(@current_token)
render_message({ notice: scoped_t('auth.logout_success'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.logout_failed'), status: :unprocessable_entity })
end
end

def validate
authenticate!(halt: false)
if @current_user
render_message({ user: current_user, notice: scoped_t('auth.token_valid') })
else
render_message({ notice: scoped_t('auth.token_invalid'), status: :unauthorized })
end
end

def reset_password_instructions
if @user && @user.set_reset_passwork_token!
#TODO send email
render_message({ notice: scoped_t('auth.reset_password_sent'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.cannot_reset_password'), status: :unprocessable_entity })
end
end

def reset_password
if @user && @user.valid_reset_password_token?(request.headers['X-Access-Reset-Password-Token'])
if reset_password_params[:password].present? && @user.update(reset_password_params.merge(reset_password_token: nil, reset_password_token_sent_at: nil))
@user.clear_tokens
render_message({ notice: scoped_t('auth.password_reset_success'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.password_reset_failed'), error: @user.errors.full_messages, status: :unprocessable_entity })
end
else
render_message({ notice: scoped_t('auth.invalid_email_or_password_token'), status: :unauthorized })
end
end

def unlock_instructions
if @user && @user.locked? && @user.set_unlock_token!
#TODO send email
render_message({ notice: scoped_t('auth.unlock_sent'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.cannot_unlock'), status: :unprocessable_entity })
end
end

def unlock
if @user && @user.valid_unlock_token(request.headers['X-Access-Unlock-Token'])
if @user.unlock!
@user.clear_tokens
render_message({ notice: scoped_t('auth.unlock_success'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.unlock_failed'), status: :unprocessable_entity })
end
else
render_message({ notice: scoped_t('auth.invalid_email_or_unlock_token'), status: :unauthorized })
end
end

def confirmation_instructions
if @user && !@user.confirmed? && @user.set_confirmation_token!
#TODO send email
render_message({ notice: scoped_t('auth.confirmation_sent'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.cannot_confirm'), status: :unprocessable_entity })
end
end

def confirm
if @user && @user.valid_confirmation_token?(request.headers['X-Access-Confirmation-Token'])
if @user.confirm!
@user.clear_tokens
render_message({ notice: scoped_t('auth.confirm_success'), location: login_api_auth_index_path })
else
render_message({ notice: scoped_t('auth.confirm_failed'), status: :unprocessable_entity })
end
else
render_message({ notice: scoped_t('auth.invalid_email_or_confirmation_token'), status: :unauthorized })
end
end

private

def set_user_from_request
@user = User.where(email: request.headers['X-Access-Email']).first
end

def set_user
@user = User.where(email: user_params[:email]).first
end

def reset_password_params
params[:user].present? ? params.require(:user).permit(:password, :password_confirmation) : {}
end

def user_params
params[:user].present? ? params.require(:user).permit(:name, :email, :password, :password_confirmation) : {}
end
end
50 changes: 39 additions & 11 deletions app/controllers/api/v1/base_controller.rb
Expand Up @@ -3,27 +3,55 @@ class Api::V1::BaseController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/vnd.pixomatix.v1' }
before_action :set_cors_request_header

respond_to :json
before_action :set_locale
before_action :set_current_token
before_action :authenticate!

rescue_from Exception, with: :generic_exception
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found

private

def record_not_found(error)
respond_to do |format|
format.json { render :json => {:error => error.message}, :status => 404 }
end
def scoped_t(string, options = {})
I18n.t("api.v1.#{string}", options)
end

def render_message(message)
status = message.delete(:status) || :ok
render json: message, status: status
end

def generic_exception(e)
Rails.logger.debug ([e.message] + e.backtrace.first(35)).join("\n")
render_message({ error: e.message, notice: scoped_t('base.internal_server_error'), status: :internal_server_error })
end

def get_locale_from_request
(request.headers['Accept-Language'] || '').scan(/^([a-z]{2})-/).flatten.first
end

def generic_exception(error)
respond_to do |format|
format.json { render :json => {:error => error.message}, :status => 500 }
end
def set_locale
I18n.locale = get_locale_from_request || I18n.default_locale
end

def set_cors_request_header
response.headers['Access-Control-Allow-Origin'] = '*'
end

def authenticate!(options = { halt: true })
user = User.where(email: request.headers['X-Access-Email']).first
@current_user = user if user && user.valid_auth_token?(@current_token)
render_message({ notice: scoped_t('base.invalid_session'), location: login_api_auth_index_path, status: :unauthorized }) if !@current_user && options[:halt]
end

def set_current_token
@current_token = request.headers['X-Access-Token']
end

def current_user
@current_user
end

def current_token
@current_token
end
end
1 change: 1 addition & 0 deletions app/controllers/api/v1/images_controller.rb
@@ -1,4 +1,5 @@
class Api::V1::ImagesController < Api::V1::BaseController
before_action :authenticate!, only: [] #FIXME remove this later to enable authentication
before_action :set_image, only: [:show, :galleries, :images, :image, :parent]

# GET /images.json
Expand Down
23 changes: 0 additions & 23 deletions app/controllers/api/v1/sessions_controller.rb

This file was deleted.