##// END OF EJS Templates
Added an API token for each User to use when making API requests. (#3920)...
Eric Davis -
r3103:aa9951b38b27
parent child
Show More
@@ -0,0 +1,13
1 class AddApiKeysForUsers < ActiveRecord::Migration
2 def self.up
3 say_with_time("Generating API keys for active users") do
4 User.active.all(:include => :api_token).each do |user|
5 user.api_key
6 end
7 end
8 end
9
10 def self.down
11 # No-op
12 end
13 end
@@ -108,6 +108,19 class MyController < ApplicationController
108 redirect_to :action => 'account'
108 redirect_to :action => 'account'
109 end
109 end
110
110
111 # Create a new API key
112 def reset_api_key
113 if request.post?
114 if User.current.api_token
115 User.current.api_token.destroy
116 User.current.reload
117 end
118 User.current.api_key
119 flash[:notice] = l(:notice_api_access_key_reseted)
120 end
121 redirect_to :action => 'account'
122 end
123
111 # User's page layout configuration
124 # User's page layout configuration
112 def page_layout
125 def page_layout
113 @user = User.current
126 @user = User.current
@@ -39,6 +39,7 class User < Principal
39 has_many :changesets, :dependent => :nullify
39 has_many :changesets, :dependent => :nullify
40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
42 belongs_to :auth_source
43 belongs_to :auth_source
43
44
44 # Active non-anonymous users scope
45 # Active non-anonymous users scope
@@ -192,6 +193,12 class User < Principal
192 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
193 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
193 token.value
194 token.value
194 end
195 end
196
197 # Return user's API key (a 40 chars long string), used to access the API
198 def api_key
199 token = self.api_token || Token.create(:user => self, :action => 'api')
200 token.value
201 end
195
202
196 # Return an array of project ids for which the user has explicitly turned mail notifications on
203 # Return an array of project ids for which the user has explicitly turned mail notifications on
197 def notified_projects_ids
204 def notified_projects_ids
@@ -210,6 +217,11 class User < Principal
210 token && token.user.active? ? token.user : nil
217 token && token.user.active? ? token.user : nil
211 end
218 end
212
219
220 def self.find_by_api_key(key)
221 token = Token.find_by_action_and_value('api', key)
222 token && token.user.active? ? token.user : nil
223 end
224
213 # Makes find_by_mail case-insensitive
225 # Makes find_by_mail case-insensitive
214 def self.find_by_mail(mail)
226 def self.find_by_mail(mail)
215 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
227 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
@@ -2,7 +2,25
2
2
3 <p><%=l(:field_login)%>: <strong><%= @user.login %></strong><br />
3 <p><%=l(:field_login)%>: <strong><%= @user.login %></strong><br />
4 <%=l(:field_created_on)%>: <%= format_time(@user.created_on) %></p>
4 <%=l(:field_created_on)%>: <%= format_time(@user.created_on) %></p>
5
6
7 <h4><%= l(:label_feeds_access_key) %></h4>
8
9 <p>
5 <% if @user.rss_token %>
10 <% if @user.rss_token %>
6 <p><%= l(:label_feeds_access_key_created_on, distance_of_time_in_words(Time.now, @user.rss_token.created_on)) %>
11 <%= l(:label_feeds_access_key_created_on, distance_of_time_in_words(Time.now, @user.rss_token.created_on)) %>
7 (<%= link_to l(:button_reset), {:action => 'reset_rss_key'}, :method => :post %>)</p>
12 <% else %>
13 <%= l(:label_missing_feeds_access_key) %>
14 <% end %>
15 (<%= link_to l(:button_reset), {:action => 'reset_rss_key'}, :method => :post %>)
16 </p>
17
18 <h4><%= l(:label_api_access_key) %></h4>
19 <p>
20 <% if @user.api_token %>
21 <%= l(:label_api_access_key_created_on, distance_of_time_in_words(Time.now, @user.api_token.created_on)) %>
22 <% else %>
23 <%= l(:label_missing_api_access_key) %>
8 <% end %>
24 <% end %>
25 (<%= link_to l(:button_reset), {:action => 'reset_api_key'}, :method => :post %>)
26 </p>
@@ -51,6 +51,18
51 <p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
51 <p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
52 <% end %>
52 <% end %>
53 </div>
53 </div>
54
55 <% if @user.api_token %>
56 <h3><%=l(:label_api_access_key) %></h3>
57 <div class="box">
58 <p>
59 <%= link_to_function(l(:text_show), "$('api-access-key').show();")%>
60 <pre id='api-access-key'><%= @user.api_key %></pre>
61 </p>
62 <%= javascript_tag("$('api-access-key').hide();") %>
63 </div>
64 <% end %>
65
54 </div>
66 </div>
55 <% end %>
67 <% end %>
56
68
@@ -142,6 +142,7 en:
142 notice_email_sent: "An email was sent to {{value}}"
142 notice_email_sent: "An email was sent to {{value}}"
143 notice_email_error: "An error occurred while sending mail ({{value}})"
143 notice_email_error: "An error occurred while sending mail ({{value}})"
144 notice_feeds_access_key_reseted: Your RSS access key was reset.
144 notice_feeds_access_key_reseted: Your RSS access key was reset.
145 notice_api_access_key_reseted: Your API access key was reset.
145 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
146 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
146 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
147 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
147 notice_account_pending: "Your account was created and is now pending administrator approval."
148 notice_account_pending: "Your account was created and is now pending administrator approval."
@@ -668,6 +669,8 en:
668 label_language_based: Based on user's language
669 label_language_based: Based on user's language
669 label_sort_by: "Sort by {{value}}"
670 label_sort_by: "Sort by {{value}}"
670 label_send_test_email: Send a test email
671 label_send_test_email: Send a test email
672 label_feeds_access_key: RSS access key
673 label_missing_feeds_access_key: Missing a RSS access key
671 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
674 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
672 label_module_plural: Modules
675 label_module_plural: Modules
673 label_added_time_by: "Added by {{author}} {{age}} ago"
676 label_added_time_by: "Added by {{author}} {{age}} ago"
@@ -729,6 +732,9 en:
729 label_copy_target: Target
732 label_copy_target: Target
730 label_copy_same_as_target: Same as target
733 label_copy_same_as_target: Same as target
731 label_display_used_statuses_only: Only display statuses that are used by this tracker
734 label_display_used_statuses_only: Only display statuses that are used by this tracker
735 label_api_access_key: API access key
736 label_missing_api_access_key: Missing an API access key
737 label_api_access_key_created_on: "API access key created {{value}} ago"
732
738
733 button_login: Login
739 button_login: Login
734 button_submit: Submit
740 button_submit: Submit
@@ -836,6 +842,7 en:
836 text_wiki_page_nullify_children: "Keep child pages as root pages"
842 text_wiki_page_nullify_children: "Keep child pages as root pages"
837 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
843 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
838 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
844 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
845 text_show: Show
839
846
840 default_role_manager: Manager
847 default_role_manager: Manager
841 default_role_developper: Developer
848 default_role_developper: Developer
@@ -163,4 +163,38 class MyControllerTest < ActionController::TestCase
163 should_redirect_to('my account') {'/my/account' }
163 should_redirect_to('my account') {'/my/account' }
164 end
164 end
165 end
165 end
166
167 context "POST to reset_api_key" do
168 context "with an existing api_token" do
169 setup do
170 @previous_token_value = User.find(2).api_key # Will generate one if it's missing
171 post :reset_api_key
172 end
173
174 should "destroy the existing token" do
175 assert_not_equal @previous_token_value, User.find(2).api_key
176 end
177
178 should "create a new token" do
179 assert User.find(2).api_token
180 end
181
182 should_set_the_flash_to /reset/
183 should_redirect_to('my account') {'/my/account' }
184 end
185
186 context "with no api_token" do
187 setup do
188 assert_nil User.find(2).api_token
189 post :reset_api_key
190 end
191
192 should "create a new token" do
193 assert User.find(2).api_token
194 end
195
196 should_set_the_flash_to /reset/
197 should_redirect_to('my account') {'/my/account' }
198 end
199 end
166 end
200 end
@@ -126,7 +126,9 class UserTest < ActiveSupport::TestCase
126 assert !anon.new_record?
126 assert !anon.new_record?
127 assert_kind_of AnonymousUser, anon
127 assert_kind_of AnonymousUser, anon
128 end
128 end
129
129
130 should_have_one :rss_token
131
130 def test_rss_key
132 def test_rss_key
131 assert_nil @jsmith.rss_token
133 assert_nil @jsmith.rss_token
132 key = @jsmith.rss_key
134 key = @jsmith.rss_key
@@ -135,7 +137,55 class UserTest < ActiveSupport::TestCase
135 @jsmith.reload
137 @jsmith.reload
136 assert_equal key, @jsmith.rss_key
138 assert_equal key, @jsmith.rss_key
137 end
139 end
140
138
141
142 should_have_one :api_token
143
144 context "User#api_key" do
145 should "generate a new one if the user doesn't have one" do
146 user = User.generate_with_protected!(:api_token => nil)
147 assert_nil user.api_token
148
149 key = user.api_key
150 assert_equal 40, key.length
151 user.reload
152 assert_equal key, user.api_key
153 end
154
155 should "return the existing api token value" do
156 user = User.generate_with_protected!
157 token = Token.generate!(:action => 'api')
158 user.api_token = token
159 assert user.save
160
161 assert_equal token.value, user.api_key
162 end
163 end
164
165 context "User#find_by_api_key" do
166 should "return nil if no matching key is found" do
167 assert_nil User.find_by_api_key('zzzzzzzzz')
168 end
169
170 should "return nil if the key is found for an inactive user" do
171 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
172 token = Token.generate!(:action => 'api')
173 user.api_token = token
174 user.save
175
176 assert_nil User.find_by_api_key(token.value)
177 end
178
179 should "return the user if the key is found for an active user" do
180 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
181 token = Token.generate!(:action => 'api')
182 user.api_token = token
183 user.save
184
185 assert_equal user, User.find_by_api_key(token.value)
186 end
187 end
188
139 def test_roles_for_project
189 def test_roles_for_project
140 # user with a role
190 # user with a role
141 roles = @jsmith.roles_for_project(Project.find(1))
191 roles = @jsmith.roles_for_project(Project.find(1))
General Comments 0
You need to be logged in to leave comments. Login now