Commit 8373e2f6227bd0a3d9d7a53f0ccf2569565bb65f
Committed by
Rodrigo Souto
1 parent
c4728894
Exists in
master
and in
29 other branches
adding authentication and entity mananegement
Showing
10 changed files
with
302 additions
and
37 deletions
Show diff stats
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
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 | ... | ... |