mirror of
https://github.com/neocities/neocities.git
synced 2025-04-24 17:22:35 +02:00
API key support
This commit is contained in:
parent
73ec613283
commit
1274e9fa63
8 changed files with 93 additions and 17 deletions
29
app/api.rb
29
app/api.rb
|
@ -148,24 +148,31 @@ def init_api_credentials
|
||||||
auth = request.env['HTTP_AUTHORIZATION']
|
auth = request.env['HTTP_AUTHORIZATION']
|
||||||
|
|
||||||
begin
|
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
|
rescue
|
||||||
api_error_invalid_auth
|
api_error_invalid_auth
|
||||||
end
|
end
|
||||||
|
|
||||||
if Site.valid_login? user, pass
|
if defined?(api_key) && !api_key.blank?
|
||||||
site = Site[username: user]
|
site = Site[api_key: api_key]
|
||||||
|
elsif defined?(user) && defined?(pass)
|
||||||
if site.nil? || site.is_banned
|
site = Site.get_site_from_login user, pass
|
||||||
api_error_invalid_auth
|
|
||||||
end
|
|
||||||
|
|
||||||
DB['update sites set api_calls=api_calls+1 where id=?', site.id].first
|
|
||||||
|
|
||||||
session[:id] = site.id
|
|
||||||
else
|
else
|
||||||
api_error_invalid_auth
|
api_error_invalid_auth
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def api_success(message_or_obj)
|
def api_success(message_or_obj)
|
||||||
|
|
|
@ -168,6 +168,17 @@ post '/settings/:username/custom_domain' do
|
||||||
end
|
end
|
||||||
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
|
post '/settings/change_password' do
|
||||||
require_login
|
require_login
|
||||||
|
|
||||||
|
|
9
migrations/103_sites_api_key.rb
Normal file
9
migrations/103_sites_api_key.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Sequel.migration do
|
||||||
|
up {
|
||||||
|
DB.add_column :sites, :api_key, :text
|
||||||
|
}
|
||||||
|
|
||||||
|
down {
|
||||||
|
DB.drop_column :sites, :api_key
|
||||||
|
}
|
||||||
|
end
|
|
@ -288,11 +288,8 @@ class Site < Sequel::Model
|
||||||
|
|
||||||
def get_site_from_login(username_or_email, plaintext)
|
def get_site_from_login(username_or_email, plaintext)
|
||||||
site = get_with_identifier username_or_email
|
site = get_with_identifier username_or_email
|
||||||
|
return nil if site.nil? || site.is_deleted || site.is_banned || !site.valid_password?(plaintext)
|
||||||
return false if site.nil?
|
site
|
||||||
return false if site.is_deleted
|
|
||||||
return false if site.is_banned
|
|
||||||
site.valid_password?(plaintext) ? site : nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def bcrypt_cost
|
def bcrypt_cost
|
||||||
|
@ -1505,6 +1502,11 @@ class Site < Sequel::Model
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def generate_api_key!
|
||||||
|
self.api_key = SecureRandom.hex(16)
|
||||||
|
save_changes validate: false
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def store_file(path, uploaded, opts={})
|
def store_file(path, uploaded, opts={})
|
||||||
|
|
|
@ -217,6 +217,26 @@ describe 'api upload' do
|
||||||
res[:error_type].must_equal 'missing_files'
|
res[:error_type].must_equal 'missing_files'
|
||||||
end
|
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
|
=begin
|
||||||
# Getting too slow to run this test
|
# Getting too slow to run this test
|
||||||
it 'fails with too many files' do
|
it 'fails with too many files' do
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
<li><a href="#username" data-toggle="tab">Change Site (User) Name</a></li>
|
<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="#tipping" data-toggle="tab">Tipping</a></li>
|
||||||
|
|
||||||
|
<li><a href="#api_key" data-toggle="tab">API Key</a></li>
|
||||||
|
|
||||||
<% if @site.admin_nsfw != true %>
|
<% if @site.admin_nsfw != true %>
|
||||||
<li><a href="#nsfw" data-toggle="tab">18+</a></li>
|
<li><a href="#nsfw" data-toggle="tab">18+</a></li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -53,7 +55,9 @@
|
||||||
<div class="tab-pane" id="tipping">
|
<div class="tab-pane" id="tipping">
|
||||||
<%== erb :'settings/site/tipping' %>
|
<%== erb :'settings/site/tipping' %>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane" id="api_key">
|
||||||
|
<%== erb :'settings/site/api_key' %>
|
||||||
|
</div>
|
||||||
<% if @site.admin_nsfw != true %>
|
<% if @site.admin_nsfw != true %>
|
||||||
<div class="tab-pane" id="nsfw">
|
<div class="tab-pane" id="nsfw">
|
||||||
<%== erb :'settings/site/nsfw' %>
|
<%== erb :'settings/site/nsfw' %>
|
||||||
|
|
4
views/settings/site/_api_key_form.erb
Normal file
4
views/settings/site/_api_key_form.erb
Normal 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>
|
19
views/settings/site/api_key.erb
Normal file
19
views/settings/site/api_key.erb
Normal 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 %>
|
Loading…
Add table
Reference in a new issue