Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
class ApplicationController < ActionController::API
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ApiAuthenticatable

include Bolognese::DoiUtils
include Bolognese::Utils

attr_accessor :username, :password
attr_accessor :username, :password, :bearer

before_action :set_raven_context
after_action :set_consumer_header

# check that username and password exist
# store them in instance variables used for calling MDS API
def authenticate_user_with_basic_auth!
@username, @password = ActionController::HttpAuthentication::Basic::user_name_and_password(request)
# check that Basic or Bearer credentials exist
# store them in instance variables used for calling DataCite API
def authenticate_request!
@bearer = bearer_token_from_request
return if @bearer.present?

request_http_basic_authentication(ENV["REALM"], "An Authentication object was not found in the SecurityContext") if @username.blank? || @password.blank?
@username, @password = basic_credentials_from_request
return if @username.present? && (@password.present? || self.class.api_key_username?(@username))

request_http_basic_authentication(ENV["REALM"], "An Authentication object was not found in the SecurityContext")
end

def set_consumer_header
Expand Down Expand Up @@ -42,7 +47,7 @@ def route_not_found
end

if status == 401
response.headers["WWW-Authenticate"] = "Basic realm=\"#{ENV['REALM']}\""
response.headers["WWW-Authenticate"] = "Basic realm=\"#{ENV['REALM']}\", Bearer realm=\"#{ENV['REALM']}\""
response.headers.delete_if { |key| key == "X-Credential-Username" }
message = "Bad credentials"
elsif status == 403
Expand Down Expand Up @@ -82,4 +87,23 @@ def set_raven_context
)
end
end

private

def bearer_token_from_request
auth_header = request.authorization.to_s
return nil unless auth_header.match?(/\ABearer\s+/i)

auth_header.sub(/\ABearer\s+/i, "").strip.presence
end

def basic_credentials_from_request
auth_header = request.authorization.to_s
return ActionController::HttpAuthentication::Basic::user_name_and_password(request) unless auth_header.match?(/\ABasic\s+/i)
return ActionController::HttpAuthentication::Basic::user_name_and_password(request) if auth_header.start_with?("Basic ")

raw_credentials = auth_header.sub(/\ABasic\s+/i, "").strip
decoded = Base64.decode64(raw_credentials)
decoded.split(":", 2)
end
end
50 changes: 50 additions & 0 deletions app/controllers/concerns/api_authenticatable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module ApiAuthenticatable
extend ActiveSupport::Concern

class_methods do
def api_key_username?(username)
username.present? && username.length > 20 && username.match?(/\ADC\./i)
end

def maremma_auth(options = {})
return { bearer: options[:bearer] } if options[:bearer].present?

username = options[:username]
password = options[:password]
if username.present? && (password.present? || api_key_username?(username))
return { username: username, password: password.to_s }
end

nil
end

def missing_auth_response
OpenStruct.new(body: { "errors" => [{ "title" => "Authentication credentials missing" }] })
end

def with_maremma_auth(options = {})
auth = maremma_auth(options)
return missing_auth_response if auth.blank?

yield auth
end

def client_relationship(auth)
return nil unless auth.key?(:username)
return nil if api_key_username?(auth[:username])

{
"client" => {
"data" => {
"type" => "clients",
"id" => auth[:username],
},
},
}
end
end

def forward_auth_options
self.class.maremma_auth(bearer: bearer, username: username, password: password)
end
end
62 changes: 29 additions & 33 deletions app/controllers/concerns/doiable.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Doiable
extend ActiveSupport::Concern
include ApiAuthenticatable

included do
def extract_url(doi: nil, data: nil)
Expand All @@ -22,56 +23,51 @@ def extract_url(doi: nil, data: nil)
end

module ClassMethods

require "uri"

def put_doi(doi, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?
return OpenStruct.new(body: { "errors" => [{ "title" => "Not a valid HTTP(S) or FTP URL" }] }) unless /\A(http|https|ftp):\/\/[\S]+/.match(options[:url])
with_maremma_auth(options) do |auth|
return OpenStruct.new(body: { "errors" => [{ "title" => "Not a valid HTTP(S) or FTP URL" }] }) unless /\A(http|https|ftp):\/\/[\S]+/.match(options[:url])

data = {
"data" => {
"type" => "dois",
"attributes"=> {
"url" => options[:url],
"should_validate" => "true",
"source" => "mds",
"event" => "publish"
},
"relationships"=> {
"client"=> {
"data"=> {
"type"=> "clients",
"id"=> options[:username]
}
data = {
"data" => {
"type" => "dois",
"attributes"=> {
"url" => options[:url],
"should_validate" => "true",
"source" => "mds",
"event" => "publish"
}
}
}
}

url = "#{ENV['API_URL']}/dois/#{doi}"
Maremma.put(url, content_type: 'application/vnd.api+json', data: data.to_json, username: options[:username], password: options[:password])
relationships = client_relationship(auth)
data["data"]["relationships"] = relationships if relationships

url = "#{ENV['API_URL']}/dois/#{doi}"
Maremma.put(url, content_type: 'application/vnd.api+json', data: data.to_json, **auth)
end
end

def get_doi(doi, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?

url = "#{ENV['API_URL']}/dois/#{doi}/get-url"
Maremma.get(url, username: options[:username], password: options[:password])
with_maremma_auth(options) do |auth|
url = "#{ENV['API_URL']}/dois/#{doi}/get-url"
Maremma.get(url, **auth)
end
end

def delete_doi(doi, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?

url = "#{ENV['API_URL']}/dois/#{doi}"
Maremma.delete(url, username: options[:username], password: options[:password])
with_maremma_auth(options) do |auth|
url = "#{ENV['API_URL']}/dois/#{doi}"
Maremma.delete(url, **auth)
end
end

def get_dois(options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?

url = "#{ENV['API_URL']}/dois/get-dois"
Maremma.get(url, username: options[:username], password: options[:password])
with_maremma_auth(options) do |auth|
url = "#{ENV['API_URL']}/dois/get-dois"
Maremma.get(url, **auth)
end
end
end
end
68 changes: 35 additions & 33 deletions app/controllers/concerns/mediable.rb
Original file line number Diff line number Diff line change
@@ -1,57 +1,59 @@
module Mediable
extend ActiveSupport::Concern
include ApiAuthenticatable

module ClassMethods
def create_media(doi, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?
return OpenStruct.new(body: { "errors" => [{ "title" => "Media type and URL missing" }] }) unless options[:data].present?
with_maremma_auth(options) do |auth|
return OpenStruct.new(body: { "errors" => [{ "title" => "Media type and URL missing" }] }) unless options[:data].present?

media_type, url = options[:data].split("=")
media_type, url = options[:data].split("=")

data = {
"data" => {
"type" => "media",
"attributes"=> {
data = {
"data" => {
"type" => "media",
"attributes"=> {
"mediaType" => media_type,
"url" => url
"url" => url
}
}
}
}

url = "#{ENV['API_URL']}/dois/#{doi}/media"
Maremma.post(url, content_type: 'application/vnd.api+json', data: data.to_json, username: options[:username], password: options[:password])
url = "#{ENV['API_URL']}/dois/#{doi}/media"
Maremma.post(url, content_type: 'application/vnd.api+json', data: data.to_json, **auth)
end
end

def list_media(doi, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?

url = "#{ENV['API_URL']}/dois/#{doi}/media"
response = Maremma.get(url, content_type: 'application/vnd.api+json', username: options[:username], password: options[:password])
return response unless response.body["data"].present?
with_maremma_auth(options) do |auth|
url = "#{ENV['API_URL']}/dois/#{doi}/media"
response = Maremma.get(url, content_type: 'application/vnd.api+json', **auth)
return response unless response.body["data"].present?

response.body["data"] = Array.wrap(response.body["data"]).map do |m|
"#{m.dig("attributes", "mediaType").to_s}=#{m.dig("attributes", "url").to_s}"
end.join("\n")
response
response.body["data"] = Array.wrap(response.body["data"]).map do |m|
"#{m.dig("attributes", "mediaType").to_s}=#{m.dig("attributes", "url").to_s}"
end.join("\n")
response
end
end

def get_media(doi, id, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?

url = "#{ENV['API_URL']}/dois/#{doi}/media/#{id}"
response = Maremma.get(url, content_type: 'application/vnd.api+json', username: options[:username], password: options[:password])
return response unless response.body["data"].present?

m = response.body["data"]
response.body["data"] = "#{m.dig("attributes", "mediaType").to_s}=#{m.dig("attributes", "url").to_s}"
response
with_maremma_auth(options) do |auth|
url = "#{ENV['API_URL']}/dois/#{doi}/media/#{id}"
response = Maremma.get(url, content_type: 'application/vnd.api+json', **auth)
return response unless response.body["data"].present?

m = response.body["data"]
response.body["data"] = "#{m.dig("attributes", "mediaType").to_s}=#{m.dig("attributes", "url").to_s}"
response
end
end

def delete_media(doi, id, options={})
return OpenStruct.new(body: { "errors" => [{ "title" => "Username or password missing" }] }) unless options[:username].present? && options[:password].present?

url = "#{ENV['API_URL']}/dois/#{doi}/media/#{id}"
response = Maremma.delete(url, content_type: 'application/vnd.api+json', username: options[:username], password: options[:password])
with_maremma_auth(options) do |auth|
url = "#{ENV['API_URL']}/dois/#{doi}/media/#{id}"
Maremma.delete(url, content_type: 'application/vnd.api+json', **auth)
end
end
end
end
Loading
Loading