api keys: rework API key UI.
* Add an explanation of what an API key is and how to use it. * Make it possible for the site owner to view all API keys. * Remove the requirement to re-enter your password before you can view your API key (to be reworked). * Move the API key controller from maintenance/user/api_keys_controller.rb to a top level controller.
This commit is contained in:
26
app/controllers/api_keys_controller.rb
Normal file
26
app/controllers/api_keys_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
class ApiKeysController < ApplicationController
|
||||
respond_to :html, :json, :xml
|
||||
|
||||
def create
|
||||
@api_key = authorize ApiKey.new(user: CurrentUser.user)
|
||||
@api_key.save
|
||||
respond_with(@api_key, location: user_api_keys_path(CurrentUser.user.id))
|
||||
end
|
||||
|
||||
def index
|
||||
params[:search][:user_id] = params[:user_id] if params[:user_id].present?
|
||||
@api_keys = authorize ApiKey.visible(CurrentUser.user).paginated_search(params, count_pages: true)
|
||||
respond_with(@api_keys)
|
||||
end
|
||||
|
||||
def show
|
||||
@api_key = authorize ApiKey.find(params[:id])
|
||||
respond_with(@api_key)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@api_key = authorize ApiKey.find(params[:id])
|
||||
@api_key.destroy
|
||||
respond_with(@api_key, location: user_api_keys_path(CurrentUser.user.id))
|
||||
end
|
||||
end
|
||||
@@ -1,43 +0,0 @@
|
||||
module Maintenance
|
||||
module User
|
||||
class ApiKeysController < ApplicationController
|
||||
before_action :check_privilege
|
||||
before_action :authenticate!, :except => [:show]
|
||||
rescue_from ::SessionLoader::AuthenticationFailure, :with => :authentication_failed
|
||||
respond_to :html, :json, :xml
|
||||
|
||||
def view
|
||||
respond_with(CurrentUser.user, @api_key)
|
||||
end
|
||||
|
||||
def update
|
||||
@api_key.regenerate!
|
||||
respond_with(CurrentUser.user, @api_key) { |format| format.js }
|
||||
end
|
||||
|
||||
def destroy
|
||||
@api_key.destroy
|
||||
respond_with(CurrentUser.user, @api_key, location: CurrentUser.user)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def check_privilege
|
||||
raise ::User::PrivilegeError unless params[:user_id].to_i == CurrentUser.id
|
||||
end
|
||||
|
||||
def authenticate!
|
||||
if CurrentUser.user.authenticate_password(params[:user][:password])
|
||||
@api_key = CurrentUser.user.api_key || ApiKey.generate!(CurrentUser.user)
|
||||
@password = params[:user][:password]
|
||||
else
|
||||
raise ::SessionLoader::AuthenticationFailure
|
||||
end
|
||||
end
|
||||
|
||||
def authentication_failed
|
||||
redirect_to(user_api_key_path(CurrentUser.user), :notice => "Password was incorrect.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -144,4 +144,8 @@ module IconHelper
|
||||
def link_icon(**options)
|
||||
icon_tag("fas fa-link", **options)
|
||||
end
|
||||
|
||||
def plus_icon(**options)
|
||||
icon_tag("fas fa-plus", **options)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,3 +55,18 @@ footer#page-footer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A container for the main <h1> tag, with optional right-aligned action buttons */
|
||||
div.page-heading {
|
||||
display: flex;
|
||||
margin-bottom: 1em;
|
||||
|
||||
h1 {
|
||||
flex-grow: 1;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@ class ApiKey < ApplicationRecord
|
||||
validates_uniqueness_of :key
|
||||
has_secure_token :key
|
||||
|
||||
def self.generate!(user)
|
||||
create(:user_id => user.id)
|
||||
def self.visible(user)
|
||||
if user.is_owner?
|
||||
all
|
||||
else
|
||||
where(user: user)
|
||||
end
|
||||
end
|
||||
|
||||
def regenerate!
|
||||
regenerate_key
|
||||
save
|
||||
def self.search(params)
|
||||
q = search_attributes(params, :id, :created_at, :updated_at, :key, :user)
|
||||
q = q.apply_default_order(params)
|
||||
q
|
||||
end
|
||||
end
|
||||
|
||||
17
app/policies/api_key_policy.rb
Normal file
17
app/policies/api_key_policy.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
class ApiKeyPolicy < ApplicationPolicy
|
||||
def create?
|
||||
!user.is_anonymous?
|
||||
end
|
||||
|
||||
def index?
|
||||
!user.is_anonymous?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
record.user == user
|
||||
end
|
||||
|
||||
def api_attributes
|
||||
super - [:key]
|
||||
end
|
||||
end
|
||||
5
app/views/api_keys/_secondary_links.html.erb
Normal file
5
app/views/api_keys/_secondary_links.html.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
<% content_for(:secondary_links) do %>
|
||||
<%= subnav_link_to "Listing", user_api_keys_path(CurrentUser.user.id) %>
|
||||
<%= subnav_link_to "New", user_api_keys_path(CurrentUser.user.id), method: :post %>
|
||||
<%= subnav_link_to "Help", wiki_page_path("help:api") %>
|
||||
<% end %>
|
||||
63
app/views/api_keys/index.html.erb
Normal file
63
app/views/api_keys/index.html.erb
Normal file
@@ -0,0 +1,63 @@
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
<div id="c-api-keys">
|
||||
<div id="a-index" class="fixed-width-container">
|
||||
<div class="page-heading">
|
||||
<h1>API Keys</h1>
|
||||
|
||||
<%= link_to user_api_keys_path(CurrentUser.user.id), class: "button-primary", method: :post do %>
|
||||
<%= plus_icon %> Add
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if params[:user_id].present? %>
|
||||
<div class="prose">
|
||||
<p>An API key is used to give programs access to your <%= Danbooru.config.canonical_app_name %> account.</p>
|
||||
|
||||
<p>If you're a developer, you can use an API key to access the
|
||||
<%= link_to_wiki "#{Danbooru.config.canonical_app_name} API", "help:api" %>. If you're not a
|
||||
developer, you probably don't need an API key.</p>
|
||||
|
||||
<p><strong>Your API key is like your password</strong>. Anyone who has it has full access to
|
||||
your account. Don't give your API key to apps or people you don't trust, and don't post your
|
||||
API key in public locations.</p>
|
||||
|
||||
<p>Example usage:
|
||||
<code>
|
||||
<% if @api_keys.present? %>
|
||||
<%= profile_url(format: "json", login: CurrentUser.user.name, api_key: @api_keys.first.key) %>
|
||||
<% else %>
|
||||
<%= profile_url(format: "json", login: CurrentUser.user.name, api_key: "your_api_key_goes_here") %>
|
||||
<% end %>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<p>See the <%= link_to_wiki "API documentation", "help:api" %> to learn more.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if params[:user_id].present? && !@api_keys.present? %>
|
||||
<%= link_to "Create API key", user_api_keys_path(CurrentUser.user.id), method: :post %>
|
||||
<% else %>
|
||||
<%= table_for @api_keys, width: "100%", class: "striped autofit" do |t| %>
|
||||
<% t.column :key, td: { class: "col-expand" } %>
|
||||
|
||||
<% if !params[:user_id].present? %>
|
||||
<% t.column "User" do |api_key| %>
|
||||
<%= link_to_user api_key.user %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% t.column "Created" do |api_key| %>
|
||||
<%= time_ago_in_words_tagged api_key.created_at %>
|
||||
<% end %>
|
||||
|
||||
<% t.column column: "control" do |api_key| %>
|
||||
<%= link_to "Delete", api_key, method: :delete %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= numbered_paginator(@api_keys) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,13 +0,0 @@
|
||||
<% page_title "API Key" %>
|
||||
|
||||
<div id="c-maintenance-user-api-keys">
|
||||
<div id="a-show">
|
||||
<h1>API Key</h1>
|
||||
<p>You must re-enter your password to view or change your API key.</p>
|
||||
|
||||
<%= edit_form_for CurrentUser.user, url: view_user_api_key_path(CurrentUser.user), method: :post do |f| %>
|
||||
<%= f.input :password, :as => :password, :input_html => {:autocomplete => "off"} %>
|
||||
<%= f.button :submit, "Submit" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
<% if @api_key.errors.any? %>
|
||||
Danbooru.error("<%= j @api_key.errors.full_messages.join(', ') %>");
|
||||
<% else %>
|
||||
$("#api-key").text("<%= j @api_key.key %>");
|
||||
$("#api-key-created").html("<%= j compact_time @api_key.created_at %>");
|
||||
$("#api-key-updated").html("<%= j compact_time @api_key.updated_at %>");
|
||||
|
||||
Danbooru.notice("API key regenerated.");
|
||||
<% end %>
|
||||
@@ -1,30 +0,0 @@
|
||||
<% page_title "API Key" %>
|
||||
|
||||
<div id="c-maintenance-user-api-keys">
|
||||
<div id="a-view">
|
||||
<h1>API Key</h1>
|
||||
|
||||
<table class="striped" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>API Key</th>
|
||||
<th>Created</th>
|
||||
<th>Updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td id="api-key"><code><%= @api_key.key %></code></td>
|
||||
<td id="api-key-created"><%= compact_time @api_key.created_at %></td>
|
||||
<td id="api-key-updated"><%= compact_time @api_key.updated_at %></td>
|
||||
<td>
|
||||
<%= button_to "Regenerate", user_api_key_path(CurrentUser.user), method: :put, params: { 'user[password]': @password }, remote: true %>
|
||||
<%= button_to "Delete", user_api_key_path(CurrentUser.user), method: :delete, params: { 'user[password]': @password } %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -261,7 +261,7 @@
|
||||
<tr>
|
||||
<th>API Key</th>
|
||||
<td>
|
||||
<%= link_to (CurrentUser.api_key ? "View" : "Generate"), user_api_key_path(CurrentUser.user) %>
|
||||
<%= link_to "View", user_api_keys_path(CurrentUser.user) %>
|
||||
(<%= link_to_wiki "help", "help:api" %>)
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user