Commit 8373e2f6227bd0a3d9d7a53f0ccf2569565bb65f

Authored by Leandro Santos
Committed by Rodrigo Souto
1 parent c4728894

adding authentication and entity mananegement

app/models/article.rb
@@ -790,12 +790,6 @@ class Article < ActiveRecord::Base @@ -790,12 +790,6 @@ class Article < ActiveRecord::Base
790 true 790 true
791 end 791 end
792 792
793 - #FIXME make this test  
794 - #Define which parameters will be returned in json object  
795 - def as_json(options = {})  
796 - super(:only => [:id, :name, :body, :created_at], :methods => [:title])  
797 - end  
798 -  
799 private 793 private
800 794
801 def sanitize_tag_list 795 def sanitize_tag_list
app/models/user.rb
1 require 'digest/sha1' 1 require 'digest/sha1'
2 require 'user_activation_job' 2 require 'user_activation_job'
  3 +require 'securerandom'
3 4
4 # User models the system users, and is generated by the acts_as_authenticated 5 # User models the system users, and is generated by the acts_as_authenticated
5 # Rails generator. 6 # Rails generator.
@@ -119,6 +120,18 @@ class User < ActiveRecord::Base @@ -119,6 +120,18 @@ class User < ActiveRecord::Base
119 self.update_attribute :last_login_at, Time.now 120 self.update_attribute :last_login_at, Time.now
120 end 121 end
121 122
  123 + #FIXME make this test
  124 + def generate_private_token!
  125 + self.private_token = SecureRandom.hex
  126 + self.private_token_generated_at = DateTime.now
  127 + save(false)
  128 + end
  129 +
  130 + #FIXME make this test
  131 + def private_token_expired?
  132 + self.generate_private_token! if self.private_token.nil? || (self.private_token_generated_at + 2.weeks < DateTime.now)
  133 + end
  134 +
122 # Activates the user in the database. 135 # Activates the user in the database.
123 def activate 136 def activate
124 return false unless self.person 137 return false unless self.person
db/migrate/20140407013817_add_private_token_info_to_users.rb
1 class AddPrivateTokenInfoToUsers < ActiveRecord::Migration 1 class AddPrivateTokenInfoToUsers < ActiveRecord::Migration
2 def self.up 2 def self.up
3 - add_column :users, :private_token 3 + add_column :users, :private_token, :string
4 add_column :users, :private_token_generated_at, :datetime 4 add_column :users, :private_token_generated_at, :datetime
5 end 5 end
6 6
lib/api/api.rb
1 require 'grape' 1 require 'grape'
  2 +Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file}
2 3
3 module API 4 module API
4 class API < Grape::API 5 class API < Grape::API
@@ -9,10 +10,10 @@ module API @@ -9,10 +10,10 @@ module API
9 10
10 mount V1::Articles 11 mount V1::Articles
11 mount V1::Comments 12 mount V1::Comments
  13 + mount V1::Users
  14 + mount Session
12 15
13 -# helpers APIHelpers  
14 -  
15 -# require Articles 16 + helpers APIHelpers
16 17
17 end 18 end
18 end 19 end
lib/api/entities.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +module API
  2 + module Entities
  3 + class Article < Grape::Entity
  4 + expose :id, :name, :body, :created_at
  5 +# expose :is_admin?, as: :is_admin
  6 +
  7 +# expose :avatar_url do |user, options|
  8 +# if user.avatar.present?
  9 +# user.avatar.url
  10 +# end
  11 +# end
  12 + end
  13 +
  14 + class Comment < Grape::Entity
  15 + expose :author_id, :body, :title, :created_at
  16 + end
  17 +
  18 + class User < Grape::Entity
  19 + expose :login
  20 + end
  21 +
  22 + class UserLogin < User
  23 + expose :private_token
  24 + end
  25 +
  26 + end
  27 +end
lib/api/helpers.rb 0 → 100644
@@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
  1 +module API
  2 + module APIHelpers
  3 + PRIVATE_TOKEN_PARAM = :private_token
  4 +
  5 + def current_user
  6 + private_token = params[PRIVATE_TOKEN_PARAM].to_s
  7 + @current_user ||= User.find_by_private_token(private_token)
  8 + @current_user = nil if !@current_user.nil? && @current_user.private_token_expired?
  9 + @current_user
  10 + end
  11 +
  12 + def logout
  13 + @current_user = nil
  14 + end
  15 +
  16 +
  17 +# def paginate(relation)
  18 +# per_page = params[:per_page].to_i
  19 +# paginated = relation.page(params[:page]).per(per_page)
  20 +# add_pagination_headers(paginated, per_page)
  21 +#
  22 +# paginated
  23 +# end
  24 +
  25 + def authenticate!
  26 + unauthorized! unless current_user
  27 + end
  28 +
  29 +# def authenticated_as_admin!
  30 +# forbidden! unless current_user.is_admin?
  31 +# end
  32 +#
  33 +# def authorize! action, subject
  34 +# unless abilities.allowed?(current_user, action, subject)
  35 +# forbidden!
  36 +# end
  37 +# end
  38 +#
  39 +# def can?(object, action, subject)
  40 +# abilities.allowed?(object, action, subject)
  41 +# end
  42 +
  43 + # Checks the occurrences of required attributes, each attribute must be present in the params hash
  44 + # or a Bad Request error is invoked.
  45 + #
  46 + # Parameters:
  47 + # keys (required) - A hash consisting of keys that must be present
  48 + def required_attributes!(keys)
  49 + keys.each do |key|
  50 + bad_request!(key) unless params[key].present?
  51 + end
  52 + end
  53 +
  54 + def attributes_for_keys(keys)
  55 + attrs = {}
  56 + keys.each do |key|
  57 + attrs[key] = params[key] if params[key].present? or (params.has_key?(key) and params[key] == false)
  58 + end
  59 + attrs
  60 + end
  61 +
  62 + # error helpers
  63 +
  64 + def forbidden!
  65 + render_api_error!('403 Forbidden', 403)
  66 + end
  67 +
  68 + def bad_request!(attribute)
  69 + message = ["400 (Bad request)"]
  70 + message << "\"" + attribute.to_s + "\" not given"
  71 + render_api_error!(message.join(' '), 400)
  72 + end
  73 +
  74 + def not_found!(resource = nil)
  75 + message = ["404"]
  76 + message << resource if resource
  77 + message << "Not Found"
  78 + render_api_error!(message.join(' '), 404)
  79 + end
  80 +
  81 + def unauthorized!
  82 + render_api_error!('401 Unauthorized', 401)
  83 + end
  84 +
  85 + def not_allowed!
  86 + render_api_error!('Method Not Allowed', 405)
  87 + end
  88 +
  89 + def render_api_error!(message, status)
  90 + error!({'message' => message}, status)
  91 + end
  92 +
  93 +# private
  94 +#
  95 +# def add_pagination_headers(paginated, per_page)
  96 +# request_url = request.url.split('?').first
  97 +#
  98 +# links = []
  99 +# links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page?
  100 +# links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page?
  101 +# links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first")
  102 +# links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last")
  103 +#
  104 +# header 'Link', links.join(', ')
  105 +# end
  106 +
  107 + end
  108 +end
lib/api/session.rb 0 → 100644
@@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
  1 +module API
  2 +
  3 +# require 'api/validations/uniqueness'
  4 +
  5 + # Users API
  6 + class Session < Grape::API
  7 +#params do
  8 +# requires :login, :uniqueness => true
  9 +#end
  10 +
  11 + # Login to get token
  12 + #
  13 + # Parameters:
  14 + # login (*required) - user login or email
  15 + # password (required) - user password
  16 + #
  17 + # Example Request:
  18 + # POST /session
  19 + get "/login" do
  20 +# post "/session" do
  21 +environment = nil #FIXME load the correct environment create a method in helper
  22 + user ||= User.authenticate(params[:login], params[:password], environment)
  23 +
  24 + return unauthorized! unless user
  25 + user.generate_private_token!
  26 + present user, :with => Entities::UserLogin
  27 + end
  28 +
  29 + # Create user.
  30 + #
  31 + # Parameters:
  32 + # email (required) - Email
  33 + # password (required) - Password
  34 + # name - Name
  35 + # Example Request:
  36 + # POST /users
  37 +# post do
  38 + get "register" do
  39 + required_attributes! [:email, :login, :password]
  40 + attrs = attributes_for_keys [:email, :login, :password]
  41 + attrs[:password_confirmation] = attrs[:password]
  42 + user = User.new(attrs)
  43 +begin
  44 + if user.save
  45 + present user, :with => Entities::User
  46 + else
  47 + not_found!
  48 + end
  49 +rescue
  50 +# not_found!
  51 +#FIXME See why notfound is not working
  52 +{}
  53 +end
  54 +# user
  55 + end
  56 +
  57 +
  58 +
  59 + end
  60 +end
lib/api/v1/articles.rb
1 module API 1 module API
2 module V1 2 module V1
3 - class Articles < Grape::API  
4 -  
5 - resource :articles do 3 + class Articles < Grape::API
6 4
7 - get do  
8 - first_update = DateTime.parse(params[:first_update]) if params[:first_update]  
9 - last_update = DateTime.parse(params[:last_update]) if params[:last_update] 5 + before { authenticate! }
  6 +
  7 + resource :articles do
10 8
11 - if first_update.nil?  
12 - begin_date = Article.first.created_at  
13 - end_date = last_update.nil? ? DateTime.now : last_update  
14 - else  
15 - begin_date = first_update  
16 - end_date = DateTime.now 9 + #FIXME See if it's possible to use pagination instead of DateTime control. see a way to use this pagination logic genericaly
  10 + get do
  11 + first_update = DateTime.parse(params[:first_update]) if params[:first_update]
  12 + last_update = DateTime.parse(params[:last_update]) if params[:last_update]
  13 +
  14 + if first_update.nil?
  15 + begin_date = Article.first.created_at
  16 + end_date = last_update.nil? ? DateTime.now : last_update
  17 + else
  18 + begin_date = first_update
  19 + end_date = DateTime.now
  20 + end
  21 +
  22 + limit = params[:limit].to_i
  23 + limit = 20 if limit == 0
  24 + conditions = {}
  25 + conditions[:type] = params[:content_type] if params[:content_type] #FIXME validate type
  26 + conditions[:created_at] = begin_date...end_date
  27 + present Article.find(:all, :conditions => conditions, :offset => (first_update.nil? ? 0 : 1), :limit => limit, :order => "created_at DESC"), :with => Entities::Article
  28 + end
  29 +
  30 + #FIXME load article with environment context
  31 + get ':id' do
  32 + present Article.find(params[:id]), :with => Entities::Article
17 end 33 end
18 34
19 - limit = params[:limit].to_i  
20 - limit = 20 if limit == 0  
21 - conditions = {}  
22 - conditions[:type] = params[:content_type] if params[:content_type] #FIXME validate type  
23 - conditions[:created_at] = begin_date...end_date  
24 - Article.find(:all, :conditions => conditions, :offset => (first_update.nil? ? 0 : 1), :limit => limit, :order => "created_at DESC")  
25 - end  
26 -  
27 - get ':id' do  
28 - Article.find(params[:id]) 35 + #FIXME load article with environment context
  36 + get ':id/children' do
  37 + present Article.find(params[:id]).children, :with => Entities::Article
  38 + end
  39 +
  40 + #FIXME load article with environment context
  41 + get ':id/children/:child_id' do
  42 + present Article.find(params[:id]).children.find(params[:child_id]), :with => Entities::Article
  43 + end
  44 +
  45 +
29 end 46 end
  47 +
30 end 48 end
31 -  
32 - end  
33 end 49 end
34 end 50 end
lib/api/v1/comments.rb
1 module API 1 module API
2 module V1 2 module V1
3 class Comments < Grape::API 3 class Comments < Grape::API
  4 +
  5 + before { authenticate! }
4 6
5 resource :articles do 7 resource :articles do
6 - 8 + #FIXME make the pagination
  9 + #FIXME put it on environment context
7 get ":id/comments" do 10 get ":id/comments" do
8 - Article.find(params[:id]).comments 11 + present Article.find(params[:id]).comments, :with => Entities::Comment
9 end 12 end
10 13
11 get ":id/comments/:comment_id" do 14 get ":id/comments/:comment_id" do
12 - Article.find(params[:id]).comments.find(params[:comment_id]) 15 + present Article.find(params[:id]).comments.find(params[:comment_id]), :with => Entities::Comment
13 end 16 end
14 17
15 end 18 end
lib/api/v1/users.rb 0 → 100644
@@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
  1 +module API
  2 + module V1
  3 + class Users < Grape::API
  4 +
  5 + before { authenticate! }
  6 +
  7 + resource :users do
  8 +
  9 + #FIXME make the pagination
  10 + #FIXME put it on environment context
  11 +# get do
  12 +# Users.all
  13 +# end
  14 +
  15 + get ":id" do
  16 + present Article.find(params[:id]).comments.find(params[:comment_id]), :with => Entities::User
  17 + end
  18 +
  19 + # Create user.
  20 + #
  21 + # Parameters:
  22 + # email (required) - Email
  23 + # password (required) - Password
  24 + # name - Name
  25 + # Example Request:
  26 + # POST /users
  27 +# post do
  28 + get do
  29 +# authenticated_as_admin!
  30 + required_attributes! [:email, :login, :password]
  31 + attrs = attributes_for_keys [:email, :login, :password]
  32 + user = User.new(attrs)
  33 + if user.save
  34 + present user, :with => Entities::User
  35 + else
  36 + not_found!
  37 + end
  38 + end
  39 + end
  40 +
  41 + end
  42 + end
  43 +end