class RavenDBManager

Constants

NAME_PATTERN
PASSWORD_CHARS

Public Class Methods

create(server_url, api_key) click to toggle source
# File lib/ravendb-manager.rb, line 17
def self.create(server_url, api_key)
        uri = URI.parse(server_url)
        api_key_parts = api_key.split('/')

        return RavenDBManager.new(uri.host, uri.port, api_key_parts[0], api_key_parts[1])
end
new(hostname, port, api_key_name, api_key_secret) click to toggle source
# File lib/ravendb-manager.rb, line 24
def initialize(hostname, port, api_key_name, api_key_secret)
        raise 'hostname is empty' unless !hostname.to_s.empty?
        raise 'port is invalid' unless port.to_i != 0

        raise 'api_key_name empty' unless !api_key_name.to_s.empty?
        raise 'api_key_secret empty' unless !api_key_secret.to_s.empty?

        @hostname = hostname.to_s
        @port = port.to_i

        @uri = "http://#{@hostname}:#{port}"
        @oauth_endpoint = URI.parse("#{@uri}/OAuth/API-Key")
        
        @api_key_name = api_key_name.to_s
        @api_key_secret = api_key_secret.to_s
        @bearer_token = get_bearer_token
end

Public Instance Methods

add_db_to_api_key(api_key_name, db_name, admin = false, readonly = false) click to toggle source
# File lib/ravendb-manager.rb, line 218
def add_db_to_api_key(api_key_name, db_name, admin = false, readonly = false)
        raise "Invalid ApiKey name #{api_key_name}" unless is_valid_string?(api_key_name)
        raise "Invalid database name #{db_name}" unless is_valid_string?(db_name)
        raise "Invalid parameter admin" unless !!admin == admin
        raise "Invalid parameter readonly" unless !!readonly == readonly

        key = "Raven/ApiKeys/#{api_key_name}"
        document = get_document('<system>', key)
        raise "API-Key #{api_key_name} doesn't exist" if document.nil?
        document['Databases'] = document['Databases'].select {|db| db['TenantId'] != db_name}
        document['Databases'].push({'TenantId' => db_name, 'admin' => admin, 'readonly' => readonly})
        put_document('<system>', key, document)
        return nil
end
alerts(database = '<system>') click to toggle source
# File lib/ravendb-manager.rb, line 86
def alerts(database = '<system>')
        raise "Invalid database name #{database}" unless is_valid_string?(database)

        doc = get_document(database, 'Raven/Alerts')
        return doc.nil? ? [] : doc['Alerts']
end
bearer_token() click to toggle source
# File lib/ravendb-manager.rb, line 42
def bearer_token
        @bearer_token
end
compact_database(database) click to toggle source
# File lib/ravendb-manager.rb, line 172
def compact_database(database)
        raise "Invalid database name #{database}" unless is_valid_string?(database)

        response = http_post("/admin/compact?database=#{database}")
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)['OperationId']
end
create_api_key(name, secret = generate_secret) click to toggle source

Backup …

# File lib/ravendb-manager.rb, line 203
def create_api_key(name, secret = generate_secret)
        raise "Invalid ApiKey name #{name}" unless is_valid_string?(name)
        raise "Invalid secret #{secret}" unless is_valid_string?(secret)

        document = {
                'Enabled' => true,
                'Name' => name,
                'Secret' => secret,
                'Databases': []
        }
        key = "Raven/ApiKeys/#{name}"
        put_document('<system>', key, document)
        return "#{name}/#{document['Secret']}"
end
create_database(name, useVoron = false) click to toggle source
# File lib/ravendb-manager.rb, line 93
def create_database(name, useVoron = false)
        raise "Invalid database name #{name}" unless is_valid_string?(name)
        raise "Invalid parameter useVoron" unless !!useVoron == useVoron

        db_document = { 
                'Id' => name, 
                'Settings' => {
                        'Raven/ActiveBundles' => "", 
                        'Raven/DataDir' => "~/#{name}",
                        'Raven/StorageTypeName' => useVoron ? 'voron' : 'esent'
                }, 
                'SecuredSettings'=> {}, 
                'Disabled' => false}
                .to_json

        response = http_put("/admin/databases/#{name}", db_document)
        raise get_error_string(response) unless is_success?(response)
        return nil
end
create_filesystem(name) click to toggle source
# File lib/ravendb-manager.rb, line 113
def create_filesystem(name)
        raise "Invalid filesystem name #{name}" unless is_valid_string?(name)

        fs_document = { 
                'Id' => name, 
                'Settings' => {
                        'Raven/ActiveBundles' => "", 
                        'Raven/FileSystem/DataDir' => "~/#{name}"
                }, 
                'SecuredSettings'=> {}, 
                'Disabled' => false}
                .to_json

        response = http_put("/admin/fs/#{name}", fs_document)
        raise get_error_string(response) unless is_success?(response)
        return nil
end
database_statistics(database) click to toggle source
# File lib/ravendb-manager.rb, line 187
def database_statistics(database)
        raise "Invalid database name #{database}" unless is_valid_string?(database)

        response = http_get("/databases/#{database}/stats")
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)
end
delete_document(database, key) click to toggle source
# File lib/ravendb-manager.rb, line 155
def delete_document(database, key)
        raise "Invalid database name #{database}" unless is_valid_string?(database)
        raise "Invalid key #{key}" unless key.to_s.length > 0

        response = http_delete("/databases/#{database}/docs/#{key}")
        raise get_error_string(response) unless is_success?(response)
        return nil
end
get_document(database, key) click to toggle source
# File lib/ravendb-manager.rb, line 131
def get_document(database, key)
        raise "Invalid database name #{database}" unless is_valid_string?(database)
        raise "Invalid key #{key}" unless key.to_s.length > 0

        response = http_get("/databases/#{database}/docs?id=#{key}")
        if is_success?(response)
                return JSON.parse(response.body)
        elsif response.code == '404'
                return nil
        else
                raise get_error_string(response)
        end
end
get_indexing_status(database) click to toggle source
# File lib/ravendb-manager.rb, line 164
def get_indexing_status(database)
        raise "Invalid database name #{database}" unless is_valid_string?(database)

        response = http_get("/databases/#{database}/admin/IndexingStatus")
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)['IndexingStatus']
end
get_operation_status(operation_id) click to toggle source
# File lib/ravendb-manager.rb, line 180
def get_operation_status(operation_id)
        raise "Invalid OperationId #{operation_id}" if operation_id.nil?

        response = http_get("/operation/status?id=#{operation_id}")
        return is_success?(response) ? JSON.parse(response.body) : nil
end
list_api_keys() click to toggle source
# File lib/ravendb-manager.rb, line 233
def list_api_keys
        response = http_get('/databases/<system>/streams/docs?startsWith=Raven/ApiKeys&pageSize=1024')
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)['Results'].map {|k| [ k['Name'], { :databases => k['Databases'], :secret => k['Secret']}]}.to_h
end
list_databases() click to toggle source
# File lib/ravendb-manager.rb, line 46
def list_databases
        start = 0
        databases = []

        results = []
        loop do
                response = http_get("/databases/?pageSize=1024&start=#{start}")
                raise get_error_string(response) unless is_success?(response)
                results = JSON.parse(response.body)
                databases = databases + results
                start += 1024
                break if results.length < 1024
        end

        return databases
end
list_filesystems() click to toggle source
# File lib/ravendb-manager.rb, line 63
def list_filesystems
        start = 0
        filesystems = []

        results = []
        loop do
                response = http_get("/fs/?pageSize=1024&start=#{start}")
                raise get_error_string(response) unless is_success?(response)
                results = JSON.parse(response.body)
                filesystems = filesystems + results
                start += 1024
                break if results.length < 1024
        end

        return filesystems
end
put_document(database, key, document) click to toggle source
# File lib/ravendb-manager.rb, line 145
def put_document(database, key, document)
        raise "Invalid database name #{database}" unless is_valid_string?(database)
        raise "Invalid key #{key}" unless key.to_s.length > 0
        raise "Invalid document" if document.nil?

        response = http_put("/databases/#{database}/docs/#{key}", document.to_json)
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)['Key']
end
server_statistics() click to toggle source
# File lib/ravendb-manager.rb, line 195
def server_statistics
        response = http_get("/admin/stats")
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)
end
version() click to toggle source
# File lib/ravendb-manager.rb, line 80
def version
        response = http_get('/databases/<system>/build/version')
        raise get_error_string(response) unless is_success?(response)
        return JSON.parse(response.body)
end

Private Instance Methods

create_rsa_key(modulus_base64, exponent_base64) click to toggle source
# File lib/ravendb-manager.rb, line 365
def create_rsa_key (modulus_base64, exponent_base64)
        key = OpenSSL::PKey::RSA.new
        exponent = OpenSSL::BN.new(Base64.decode64(exponent_base64).unpack("H*").first, 16)
        modulus = OpenSSL::BN.new(Base64.decode64(modulus_base64).unpack("H*").first, 16)
        key.e = exponent
        key.n = modulus
        key
end
encrypt(public_key, data) click to toggle source
# File lib/ravendb-manager.rb, line 374
def encrypt(public_key, data)
        key = SecureRandom.random_bytes(32)
        iv = SecureRandom.random_bytes(16)

        key_and_iv_encrypted = public_key.public_encrypt(key + iv, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
        
        cipher = OpenSSL::Cipher::AES256.new(:CBC)
        cipher.encrypt
        cipher.key = key
        cipher.iv = iv
        encrypted = cipher.update(data) + cipher.final
        
        Base64.encode64 (key_and_iv_encrypted + encrypted)
end
generate_secret() click to toggle source
# File lib/ravendb-manager.rb, line 244
def generate_secret
        return (16 + SecureRandom.random_number(4)).times.map { PASSWORD_CHARS[SecureRandom.random_number(PASSWORD_CHARS.length)] }.join
end
get_auth_request_hash() click to toggle source
# File lib/ravendb-manager.rb, line 337
def get_auth_request_hash
        http = Net::HTTP.new(@server_url, 8088)
        res = Net::HTTP.post_form(@oauth_endpoint, {})
        
        wwwauth_header = res.header['www-authenticate']
        wwwauth_header[7, wwwauth_header.length - 7] # Remove 'Raven '
                .split(',')
                .map{|str|
                        idx = str.index '='
                        [str[0, idx].strip.downcase, str[idx+1, str.length]]
                }
                .to_h
end
get_auth_token(response_data) click to toggle source
# File lib/ravendb-manager.rb, line 351
def get_auth_token (response_data)
        req = Net::HTTP::Post.new(@oauth_endpoint.request_uri)
        req.add_field('grant-type', 'client_credentials')
        req.body = response_data
        
        res = Net::HTTP.new(@oauth_endpoint.host, @oauth_endpoint.port).request(req)
        res.body.to_s
end
get_bearer_token() click to toggle source
# File lib/ravendb-manager.rb, line 327
def get_bearer_token
        request = get_auth_request_hash
        response = prepare_response(request['challenge'])
        public_key = create_rsa_key(request['modulus'], request['exponent'])

        data = "api key name=#{@api_key_name},challenge=#{request['challenge']},response=#{response}"
        response_data = "exponent=#{request['exponent']},modulus=#{request['modulus']},data=#{encrypt(public_key, data)}"
        get_auth_token(response_data)
end
get_error_string(http_error_response) click to toggle source
# File lib/ravendb-manager.rb, line 257
def get_error_string(http_error_response) 
        if http_error_response.methods.include?(:body) and !http_error_response.body.to_s.empty?
                return http_error_response.body
        else
                return "#{http_error_response.code} #{http_error_response.message}"
        end
end
has_token_expired?(request) click to toggle source
# File lib/ravendb-manager.rb, line 323
def has_token_expired?(request)
        return request['www-authenticate'].to_s.include?('The access token is expired')
end
http_delete(url, is_retry=false) click to toggle source
# File lib/ravendb-manager.rb, line 279
def http_delete(url, is_retry=false)
        http = Net::HTTP.new(@hostname, @port)
        request = Net::HTTP::Delete.new(URI.escape(url))
        request['Authorization'] = "Bearer #{@bearer_token}"
        
        response = http.request(request)
        if has_token_expired?(request) && !is_retry then
                @bearer_token = get_bearer_token
                return http_delete(url, true)
        end

        return response
end
http_get(url, is_retry=false) click to toggle source
# File lib/ravendb-manager.rb, line 265
def http_get(url, is_retry=false)
        http = Net::HTTP.new(@hostname, @port)
        request = Net::HTTP::Get.new(URI.escape(url))
        request['Authorization'] = "Bearer #{@bearer_token}"

        response = http.request(request)
        if has_token_expired?(response) && !is_retry then
                @bearer_token = get_bearer_token
                return http_get(url, true)
        end

        return response
end
http_post(url, document = nil, is_retry=false) click to toggle source
# File lib/ravendb-manager.rb, line 308
def http_post(url, document = nil, is_retry=false)
        http = Net::HTTP.new(@hostname, @port)
        request = Net::HTTP::Post.new(URI.escape(url))
        request['Authorization'] = "Bearer #{@bearer_token}"
        request.body = document unless document.nil?
        
        response = http.request(request)
        if has_token_expired?(request) && !is_retry then
                @bearer_token = get_bearer_token
                return http_post(url, document, true)
        end

        return response
end
http_put(url, document, is_retry=false) click to toggle source
# File lib/ravendb-manager.rb, line 293
def http_put(url, document, is_retry=false)
        http = Net::HTTP.new(@hostname, @port)
        request = Net::HTTP::Put.new(URI.escape(url))
        request['Authorization'] = "Bearer #{@bearer_token}"
        request.body = document

        response = http.request(request)
        if has_token_expired?(request) && !is_retry then
                @bearer_token = get_bearer_token
                return http_put(url, document, true)
        end

        return response
end
is_success?(http_response) click to toggle source
# File lib/ravendb-manager.rb, line 252
def is_success?(http_response)
        code = http_response.code.to_i
        return code >= 200 && code <= 299
end
is_valid_string?(str) click to toggle source
# File lib/ravendb-manager.rb, line 248
def is_valid_string?(str)
        return !NAME_PATTERN.match(str).nil?
end
prepare_response(challenge) click to toggle source
# File lib/ravendb-manager.rb, line 360
def prepare_response (challenge)
        input = challenge + ";" + @api_key_secret
        Digest::SHA1.base64digest input 
end