API key support

This commit is contained in:
Kyle Drake 2017-05-13 18:18:34 -05:00
parent 73ec613283
commit 1274e9fa63
8 changed files with 93 additions and 17 deletions

View file

@ -148,24 +148,31 @@ def init_api_credentials
auth = request.env['HTTP_AUTHORIZATION']
begin
user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':')
if bearer_match = auth.match(/^Bearer (.+)/)
api_key = bearer_match.captures.first
api_error_invalid_auth if api_key.nil? || api_key.empty?
else
user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':')
end
rescue
api_error_invalid_auth
end
if Site.valid_login? user, pass
site = Site[username: user]
if site.nil? || site.is_banned
api_error_invalid_auth
end
DB['update sites set api_calls=api_calls+1 where id=?', site.id].first
session[:id] = site.id
if defined?(api_key) && !api_key.blank?
site = Site[api_key: api_key]
elsif defined?(user) && defined?(pass)
site = Site.get_site_from_login user, pass
else
api_error_invalid_auth
end
if site.nil? || site.is_banned || site.is_deleted
api_error_invalid_auth
end
DB['update sites set api_calls=api_calls+1 where id=?', site.id].first
session[:id] = site.id
end
def api_success(message_or_obj)

View file

@ -168,6 +168,17 @@ post '/settings/:username/custom_domain' do
end
end
post '/settings/:username/generate_api_key' do
require_login
require_ownership_for_settings
is_new = current_site.api_key.nil?
current_site.generate_api_key!
msg = is_new ? "New API key has been generated." : "API key has been regenerated."
flash[:success] = msg
redirect "/settings/#{current_site.username}#api_key"
end
post '/settings/change_password' do
require_login

View file

@ -0,0 +1,9 @@
Sequel.migration do
up {
DB.add_column :sites, :api_key, :text
}
down {
DB.drop_column :sites, :api_key
}
end

View file

@ -288,11 +288,8 @@ class Site < Sequel::Model
def get_site_from_login(username_or_email, plaintext)
site = get_with_identifier username_or_email
return false if site.nil?
return false if site.is_deleted
return false if site.is_banned
site.valid_password?(plaintext) ? site : nil
return nil if site.nil? || site.is_deleted || site.is_banned || !site.valid_password?(plaintext)
site
end
def bcrypt_cost
@ -1505,6 +1502,11 @@ class Site < Sequel::Model
true
end
def generate_api_key!
self.api_key = SecureRandom.hex(16)
save_changes validate: false
end
private
def store_file(path, uploaded, opts={})

View file

@ -217,6 +217,26 @@ describe 'api upload' do
res[:error_type].must_equal 'missing_files'
end
it 'succeeds with valid api key' do
create_site
@site.api_key.must_equal nil
@site.generate_api_key!
@site.reload.api_key.wont_equal nil
header 'Authorization', "Bearer #{@site.api_key}"
post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal true
end
it 'fails with bad api key' do
create_site
@site.generate_api_key!
header 'Authorization', "Bearer zerocool"
post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'error'
res[:error_type].must_equal 'invalid_auth'
end
=begin
# Getting too slow to run this test
it 'fails with too many files' do

View file

@ -29,6 +29,8 @@
<li><a href="#username" data-toggle="tab">Change Site (User) Name</a></li>
<li><a href="#tipping" data-toggle="tab">Tipping</a></li>
<li><a href="#api_key" data-toggle="tab">API Key</a></li>
<% if @site.admin_nsfw != true %>
<li><a href="#nsfw" data-toggle="tab">18+</a></li>
<% end %>
@ -53,7 +55,9 @@
<div class="tab-pane" id="tipping">
<%== erb :'settings/site/tipping' %>
</div>
<div class="tab-pane" id="api_key">
<%== erb :'settings/site/api_key' %>
</div>
<% if @site.admin_nsfw != true %>
<div class="tab-pane" id="nsfw">
<%== erb :'settings/site/nsfw' %>

View file

@ -0,0 +1,4 @@
<form method="POST" action="/settings/<%= @site.username %>/generate_api_key">
<%== csrf_token_input_html %>
<input class="btn-Action" type="submit" value="Generate API Key">
</form>

View file

@ -0,0 +1,19 @@
<h2>API Key</h2>
<p>
An API Key can be used with the <a href="https://neocities.org/api">Neocities API</a> to allow for changes to your site without requiring login credentials. <strong>This API key allows full write access to your site. Keep it secret, keep it safe.</strong>
</p>
<% if @site.api_key %>
<p>Your API key:<br><strong><%= @site.api_key %></strong></p>
<p>
If you need to regenerate the key for some reason, you can do that below.
Keep in mind that this will make the old key no longer function.
</p>
<%== erb :'settings/site/_api_key_form', layout: false %>
<% else %>
<p>You haven't yet generated an API key, click the button to create one:</p>
<%== erb :'settings/site/_api_key_form', layout: false %>
<% end %>