-
Notifications
You must be signed in to change notification settings - Fork 650
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15567 from CartoDB/dbdirect
DB Direct Certificates Management
- Loading branch information
Showing
26 changed files
with
1,928 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
app/controllers/carto/api/dbdirect_certificate_presenter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
require 'json' | ||
|
||
module Carto | ||
module Api | ||
class DbdirectCertificatePresenter | ||
def initialize(dbdirect_certificate) | ||
@dbdirect_certificate = dbdirect_certificate | ||
end | ||
|
||
def to_poro | ||
return {} unless @dbdirect_certificate | ||
|
||
{ | ||
id: @dbdirect_certificate.id, | ||
name: @dbdirect_certificate.name, | ||
expiration: @dbdirect_certificate.expiration.to_datetime.rfc3339 | ||
} | ||
end | ||
end | ||
end | ||
end |
131 changes: 131 additions & 0 deletions
131
app/controllers/carto/api/dbdirect_certificates_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
require 'in_mem_zipper' | ||
|
||
module Carto | ||
module Api | ||
class DbdirectCertificatesController < ::Api::ApplicationController | ||
include Carto::ControllerHelper | ||
extend Carto::DefaultRescueFroms | ||
|
||
skip_before_filter :verify_authenticity_token, only: [:create], if: :zip_formatted_request? | ||
|
||
ssl_required :list, :show, :create, :destroy | ||
|
||
before_action :load_user | ||
before_action :check_permissions | ||
|
||
setup_default_rescues | ||
|
||
def index | ||
dbdirect_certificates = @user.dbdirect_certificates | ||
certificates_info = dbdirect_certificates.map do |certificate| | ||
Carto::Api::DbdirectCertificatePresenter.new(certificate).to_poro | ||
end | ||
render_jsonp(certificates_info, 200) | ||
end | ||
|
||
def show | ||
dbdirect_certificate = Carto::DbdirectCertificate.find(params[:id]) | ||
check_permissions_for_certificate(dbdirect_certificate) | ||
render_jsonp(Carto::Api::DbdirectCertificatePresenter.new(dbdirect_certificate).to_poro, 200) | ||
end | ||
|
||
def create | ||
validity_days = params[:validity].blank? ? Carto::DbdirectCertificate.default_validity : params[:validity].to_i | ||
data, cert = Carto::DbdirectCertificate.generate( | ||
user: @user, | ||
name: params[:name], | ||
passphrase: params[:pass], | ||
validity_days: validity_days, | ||
server_ca: params[:server_ca] | ||
) | ||
result = { | ||
id: cert.id, | ||
name: cert.name, # must include name since we may have changed or generated it | ||
client_key: data[:client_key], | ||
client_crt: data[:client_crt], | ||
server_ca: data[:server_ca] | ||
} | ||
|
||
respond_to do |format| | ||
format.json do | ||
render_jsonp(result, 201) | ||
end | ||
format.zip do | ||
zip_filename, zip_data = zip_certificates(result) | ||
send_data( | ||
zip_data, | ||
type: "application/zip; charset=binary; header=present", | ||
disposition: "attachment; filename=#{zip_filename}", | ||
status: 201 | ||
) | ||
end | ||
end | ||
end | ||
|
||
def destroy | ||
dbdirect_certificate = Carto::DbdirectCertificate.find(params[:id]) | ||
check_permissions_for_certificate(dbdirect_certificate) | ||
dbdirect_certificate.destroy! | ||
head :no_content | ||
end | ||
|
||
private | ||
|
||
def zip_certificates(result) | ||
username = @user.username | ||
dbproxy_host = Cartodb.get_config(:dbdirect, 'pgproxy', 'host') | ||
dbproxy_port = Cartodb.get_config(:dbdirect, 'pgproxy', 'port') | ||
certificate_id = result[:id] | ||
certificate_name = result[:name] | ||
client_key = result[:client_key] | ||
client_crt = result[:client_crt] | ||
server_ca = result[:server_ca] | ||
|
||
readme = generate_readme( | ||
certificate_id: certificate_id, | ||
certificate_name: certificate_name, | ||
username: username, | ||
dbproxy_host: dbproxy_host, | ||
dbproxy_port: dbproxy_port | ||
) | ||
filename = "#{certificate_name}.zip" | ||
|
||
zip_data = InMemZipper.zip( | ||
'README.txt' => readme, | ||
'client.key' => client_key, | ||
'client.crt' => client_crt, | ||
'server_ca.pem' => server_ca | ||
) | ||
[ filename, zip_data ] | ||
end | ||
|
||
def generate_readme(readme_params) | ||
if params[:readme].present? | ||
readme = view_context.render(inline: params[:readme], :locals => readme_params) | ||
else | ||
readme = view_context.render(template: 'carto/api/dbdirect_certificates/README.txt.erb', :locals => readme_params) | ||
end | ||
end | ||
|
||
def load_user | ||
@user = Carto::User.find(current_viewer.id) | ||
end | ||
|
||
def check_permissions | ||
# TODO: should the user be an organization owner? | ||
api_key = Carto::ApiKey.find_by_token(params["api_key"]) | ||
if api_key.present? | ||
raise UnauthorizedError unless api_key.master? | ||
raise UnauthorizedError unless api_key.user_id == @user.id | ||
end | ||
unless @user.has_feature_flag?('dbdirect') | ||
raise UnauthorizedError.new("DBDirect not enabled for user #{@user.username}") | ||
end | ||
end | ||
|
||
def check_permissions_for_certificate(dbdirect_certificate) | ||
raise UnauthorizedError unless dbdirect_certificate.user_id == @user.id | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
module Carto | ||
module Api | ||
class DbdirectIpsController < ::Api::ApplicationController | ||
include Carto::ControllerHelper | ||
extend Carto::DefaultRescueFroms | ||
|
||
ssl_required :show, :update, :destroy | ||
|
||
before_action :load_user | ||
before_action :check_permissions | ||
|
||
setup_default_rescues | ||
|
||
def show | ||
ips = @user.dbdirect_effective_ips | ||
render_jsonp({ ips: ips }, 200) | ||
end | ||
|
||
def update | ||
@user.dbdirect_effective_ips = params[:ips] | ||
@user.save! | ||
render_jsonp({ ips: @user.reload.dbdirect_effective_ips }, 201) | ||
end | ||
|
||
def destroy | ||
@user.dbdirect_effective_ips = nil | ||
@user.save! | ||
head :no_content | ||
end | ||
|
||
private | ||
|
||
def load_user | ||
@user = Carto::User.find(current_viewer.id) | ||
end | ||
|
||
def check_permissions | ||
# TODO: should the user be an organization owner? | ||
api_key = Carto::ApiKey.find_by_token(params["api_key"]) | ||
if api_key.present? | ||
raise UnauthorizedError unless api_key.master? | ||
raise UnauthorizedError unless api_key.user_id == @user.id | ||
end | ||
unless @user.has_feature_flag?('dbdirect') | ||
raise UnauthorizedError.new("DBDirect not enabled for user #{@user.username}") | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
require 'carto/dbdirect/certificate_manager' | ||
|
||
module Carto | ||
class DbdirectCertificate < ActiveRecord::Base | ||
belongs_to :user, inverse_of: :dbdirect_certificates, foreign_key: :user_id | ||
validates_uniqueness_of :name, scope: :user_id | ||
|
||
scope :expired, -> { where('expiration <=', Time.current) } | ||
scope :valid, -> { where('expiration >', Time.current) } | ||
|
||
before_destroy :revoke | ||
|
||
def self.generate(user:, name:, passphrase: nil, validity_days: nil, server_ca: true) | ||
validity_days ||= config['maximum_validity_days'] | ||
name = valid_name(user, name) | ||
|
||
certificates, arn = certificate_manager.generate_certificate( | ||
username: user.username, | ||
passphrase: passphrase, | ||
validity_days: validity_days, | ||
server_ca: server_ca | ||
) | ||
|
||
new_record = create( | ||
user_id: user.id, | ||
name: name, | ||
arn: arn, | ||
expiration: DateTime.now + validity_days # TODO: extract from cert.? | ||
) | ||
|
||
return certificates, new_record | ||
end | ||
|
||
def self.default_validity | ||
config[:maximum_validity_days] | ||
end | ||
|
||
def self.certificate_manager | ||
certificate_manager_class.new(config) | ||
end | ||
|
||
def self.certificate_manager_class | ||
Carto::Dbdirect::CertificateManager | ||
end | ||
|
||
private | ||
|
||
def revoke | ||
self.class.certificate_manager.revoke_certificate(arn: arn) | ||
end | ||
|
||
class <<self | ||
private | ||
|
||
def config | ||
Cartodb.get_config(:dbdirect, 'certificates') | ||
end | ||
|
||
def certificate_names(user) | ||
Carto::User.find(user.id).dbdirect_certificates.map(&:name) | ||
end | ||
|
||
def valid_name(user, name) | ||
name = user.username if name.blank? | ||
names = certificate_names(user) | ||
return name unless name.in?(names) | ||
|
||
max_suffix = names.map do |existing_name| | ||
match = /\A#{Regexp.escape name}_(\d+)\Z/.match(existing_name) | ||
match ? match[1].to_i : 0 | ||
end.max | ||
"#{name}_#{max_suffix + 1}" | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.