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 790 true
791 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 793 private
800 794  
801 795 def sanitize_tag_list
... ...
app/models/user.rb
1 1 require 'digest/sha1'
2 2 require 'user_activation_job'
  3 +require 'securerandom'
3 4  
4 5 # User models the system users, and is generated by the acts_as_authenticated
5 6 # Rails generator.
... ... @@ -119,6 +120,18 @@ class User < ActiveRecord::Base
119 120 self.update_attribute :last_login_at, Time.now
120 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 135 # Activates the user in the database.
123 136 def activate
124 137 return false unless self.person
... ...
db/migrate/20140407013817_add_private_token_info_to_users.rb
1 1 class AddPrivateTokenInfoToUsers < ActiveRecord::Migration
2 2 def self.up
3   - add_column :users, :private_token
  3 + add_column :users, :private_token, :string
4 4 add_column :users, :private_token_generated_at, :datetime
5 5 end
6 6  
... ...
lib/api/api.rb
1 1 require 'grape'
  2 +Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file}
2 3  
3 4 module API
4 5 class API < Grape::API
... ... @@ -9,10 +10,10 @@ module API
9 10  
10 11 mount V1::Articles
11 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 18 end
18 19 end
... ...
lib/api/entities.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 1 module API
2 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 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 46 end
  47 +
30 48 end
31   -
32   - end
33 49 end
34 50 end
... ...
lib/api/v1/comments.rb
1 1 module API
2 2 module V1
3 3 class Comments < Grape::API
  4 +
  5 + before { authenticate! }
4 6  
5 7 resource :articles do
6   -
  8 + #FIXME make the pagination
  9 + #FIXME put it on environment context
7 10 get ":id/comments" do
8   - Article.find(params[:id]).comments
  11 + present Article.find(params[:id]).comments, :with => Entities::Comment
9 12 end
10 13  
11 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 16 end
14 17  
15 18 end
... ...
lib/api/v1/users.rb 0 → 100644
... ... @@ -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
... ...