Commit 901069fd640484fd733b3744205b727d81ec2ef6
1 parent
da4c5d6f
Exists in
staging
and in
4 other branches
adding authentication and entity mananegement
Showing
10 changed files
with
302 additions
and
37 deletions
Show diff stats
app/models/article.rb
| ... | ... | @@ -730,12 +730,6 @@ class Article < ActiveRecord::Base |
| 730 | 730 | true |
| 731 | 731 | end |
| 732 | 732 | |
| 733 | - #FIXME make this test | |
| 734 | - #Define which parameters will be returned in json object | |
| 735 | - def as_json(options = {}) | |
| 736 | - super(:only => [:id, :name, :body, :created_at], :methods => [:title]) | |
| 737 | - end | |
| 738 | - | |
| 739 | 733 | private |
| 740 | 734 | |
| 741 | 735 | def sanitize_tag_list | ... | ... |
app/models/user.rb
| 1 | 1 | require 'digest/sha1' |
| 2 | +require 'securerandom' | |
| 2 | 3 | |
| 3 | 4 | # User models the system users, and is generated by the acts_as_authenticated |
| 4 | 5 | # Rails generator. |
| ... | ... | @@ -125,6 +126,18 @@ class User < ActiveRecord::Base |
| 125 | 126 | u && u.authenticated?(password) ? u : nil |
| 126 | 127 | end |
| 127 | 128 | |
| 129 | + #FIXME make this test | |
| 130 | + def generate_private_token! | |
| 131 | + self.private_token = SecureRandom.hex | |
| 132 | + self.private_token_generated_at = DateTime.now | |
| 133 | + save(false) | |
| 134 | + end | |
| 135 | + | |
| 136 | + #FIXME make this test | |
| 137 | + def private_token_expired? | |
| 138 | + self.generate_private_token! if self.private_token.nil? || (self.private_token_generated_at + 2.weeks < DateTime.now) | |
| 139 | + end | |
| 140 | + | |
| 128 | 141 | # Activates the user in the database. |
| 129 | 142 | def activate |
| 130 | 143 | return false unless self.person | ... | ... |
db/migrate/20140407013817_add_private_token_info_to_users.rb
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 | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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 | ... | ... |