...
3.1 Configuration
The configuration step involves declaring multiple tenant configurations, parameterizing the routes that will be used by our controllers (SSO, Connec etc.) and adding a metadata endpoint to expose our tenant specific configuration.
Code Block |
---|
language | ruby |
---|
title | App Initializer |
---|
linenumbers | true |
---|
|
# This piece of code should be put in an initializer.
#
# With the Maestrano SDKs, you have the ability to add as many
# marketplace configurations as you want. E.g.: you can add a configuration manifest
# for 'maestrano' and another one for 'acme-corp'.
#
# These configuration manifests only need to be added for Enterprise Tenants, which host a dedicated
# Maestrano infrastructure on their private cloud.
#
# Create a configuration manifest for the key "maestrano"
#
Maestrano.with("maestrano").configure({
# Tenant specific configuration
sso_idp: "https://maestrano.com",
account_api_host: "https://maestrano.com",
connec_api_host: "https://api-connec.maestrano.com",
# My app configuration for this tenant - note the use of the tenant key
# in the url.
# This URL will be used by Maestrano.com to send you notifications
app_webhook_path: "https://myapp.com/mno-enterprise/maestrano/connec/receive"
})
#
# Create a configuration manifest for the key "acme-corp"
#
Maestrano.with("acme-corp").configure({
# Tenant specific configuration
sso_idp: "https://saml.acme-corp.com",
account_api_host: "https://accounts.acme-corp.com",
connec_api_host: "https://api-connec.acme-corp.com",
# My app configuration for this tenant - note the use of the tenant key
# in the url.
# This URL will be used by Acme Corp to send you notifications
app_webhook_path: "https://myapp.com/mno-enterprise/acme-corp/connec/receive"
}) |
Code Block |
---|
language | ruby |
---|
title | App Routes |
---|
linenumbers | true |
---|
|
#
# Let's create parameterized routes
#
# The metadata route will be fetched by the enterprise tenants to retrieve your configuration
route "/mno-enterprise/:tenant_key/metadata" to "MetadataController" on action "show"
# The single sign-on routes will be used by enterprise tenants to trigger and complete SSO handshakes
route "/mno-enterprise/:tenant_key/saml/initialize" to "SamlSsoController" on action "initialize"
route "/mno-enterprise/:tenant_key/saml/consume" to "SamlSsoController" on action "consume"
# The Account Webhook routes notify you of groups being removed or users being removed from groups
route "/mno-enterprise/:tenant_key/account/group/:id" to "AccountWebhookController" on action "destroy_group"
route "/mno-enterprise/:tenant_key/account/group/:group_id/user/:id" to "AccountWebhookController" on action "remove_user"
# The Connec!™ webhook route will be used by enterprise tenants to POST data sharing notifications
route "/mno-enterpise/:tenant_key/connec/receive" to "ConnecWebhookController" with action "receive"
|
...
language | ruby |
---|
title | MetadataController |
---|
linenumbers | true |
---|
...
is automatically taken care of by the autoconfigure method.
If your application is environment is set up on more than one marketplaces (or MnoHub), the autoconfigure method will call the https://developer.maestrano.com/api/config/v1/marketplaces endpoint in order to retrieve the different configurations per marketplaces.
Code Block |
---|
language | ruby |
---|
title | App Initializer |
---|
linenumbers | true |
---|
|
# This piece of code should be put in an initializer.
#
Maestrano.autoconfigure({
host: "https://developer.maestrano.com",
api_path: "/api/config/v1",
environment: {
name: "[your environment nid]"
api_key: "[your environment key]"
api_secret: "[your environment secret]"
}
})
# To retrieve the configuration per marketplace you may call
Maestrano.with(marketplace)
# that will contains all the configuration settings for this marketplace
|
Code Block |
---|
language | ruby |
---|
title | App Routes |
---|
linenumbers | true |
---|
|
#
# Let's create parameterized routes
#
# The single sign-on routes will be used by enterprise tenants to trigger and complete SSO handshakes
route "/mno-enterprise/:marketplace/saml/initialize" to "SamlSsoController" on action "initialize"
route "/mno-enterprise/:marketplace/saml/consume" to "SamlSsoController" on action "consume"
# The Account Webhook routes notify you of groups being removed or users being removed from groups
route "/mno-enterprise/:marketplace/account/group/:id" to "AccountWebhookController" on action "destroy_group"
route "/mno-enterprise/:marketplace/account/group/:group_id/user/:id" to "AccountWebhookController" on action "remove_user"
# The Connec!™ webhook route will be used by enterprise tenants to POST data sharing notifications
route "/mno-enterpise/:marketplace/connec/receive" to "ConnecWebhookController" with action "receive"
|
3.2 Single Sign-On
Info |
---|
title | OpenID also available! |
---|
|
The example below assumes you are using our SDK for Single Sign-On which is based on SAML 2.0. Maestrano also has an OpenID provider available. Want to know more? Just checkout our OpenID guide. |
Code Block |
---|
language | ruby |
---|
title | SamlSSOController |
---|
linenumbers | true |
---|
|
#
# This controller handles the Single Sign-On handshake
#
class SamlSsoController
# The 'initialize' controller action responds the following route
# GET /mno-enterprise/:tenant_keymarketplace/saml/initialize
#
# The goal of this action is to trigger the Single Sign-On handshake
# between the tenant platform and your application
function initialize
# Retrieve the tenant keymarketplace from the URL parameters
tenant_keymarketplace = params['tenant_key']
redirect_to MaestranoSamlRequest.with(tenant_keymarketplace).new(params).redirect_url
end
# The 'initialize' controller action responds to the following route
# POST /mno-enterprise/:tenant_keymarketplace/saml/consume
function consume
# Retrieve the tenant key from the URL parameters
tenant_keymarketplace = params['tenant_keymarketplace']
# Process the response
saml_response = Maestrano::Saml::Response.with(tenant_keymarketplace).new(params[:SAMLResponse])
# Reject if invalid
unless saml_response.is_valid?
redirect_to "/some/error/path"
end
# Extract information from the response
user_attributes = Maestrano::SSO::BaseUser.new(marketplace, saml_response).to_hash_or_associative_array
group_attributes = Maestrano::SSO::BaseGroup.new(marketplace, saml_response).to_hash_or_associative_array
# Find/create the user and the organization
# The creation or retrieval of records should be scoped to a specific provider (tenant_keymarketplace)
user = User.find_or_create_for_maestrano_tenant(user_attributes,tenant_key marketplace)
organization = Organization.find_or_create_for_maestrano_tenant(group_attributes,tenant_key marketplace)
# Add user to the organization if not there already
unless organization.has_member?(user)
organization.add_member(user)
end
# Sign the user in and redirect to application root
# To be customised depending on how you handle user
# sign in and
sign_user_in(user)
redirect_to "/some/post-login/path"
end
end |
...
Code Block |
---|
language | ruby |
---|
title | WebhookAccountController |
---|
linenumbers | true |
---|
|
#
# This controller handles notification of people leaving a group (remove_user action) or companies
# cancelling their subscription to your service (destroy_group)
class WebhookAccountController
# The 'destroy_group' controller action responds the following route
# DESTROY /mno-enterprise/:tenant_keymarketplace/account/group/:id
function destroy_group
# Retrieve the request parameters
marketplace = params[:marketplace]
group_uid = params[:id]
# Authenticate request as usual
unless Maestrano.with(tenant_keymarketplace).authenticate(http_basic['login'],http_basic['password'])
return render json: "Unauthorized, code: '401'
end
# Retrieve the request parameters
tenant_key = params[:tenant_key]
group_uid = params[:id]
# Retrieve the group/company
organization = Organization.find_by_tenant_and_uid(tenant_key,find_by(marketplace: marketplace, uid: group_uid)
# Destroy it
organization.destroy
end
# The 'destroy_group' controller action responds the following route
# DESTROY /mno-enterprise/:tenant_keymarketplace/account/group/:group_id/user/:id
function remove_user
# Authenticate request as usual
unless Maestrano.with(tenant_key).authenticate(http_basic['login'],http_basic['password'])
# Retrieve renderthe json: "Unauthorized, code: '401'request parameters
endmarketplace = params[:marketplace]
group_uid = # Retrieve the request parameters
params[:group_id]
user_uid = params[:id]
tenant_key = params[:tenant_key]
# Authenticate request as usual
group_uid = params[:group_id] unless Maestrano.with(marketplace).authenticate(http_basic['login'],http_basic['password'])
user_uid = params[:id] return render json: "Unauthorized, code: '401'
end
# Retrieve the group/company as well as the user
organization = Organization.find_by_tenant_and_uid(tenant_key,_by(marketplace: marketplace, uid: group_uid)
user = User.find_by_tenant_and_uid(tenant_key,(marketplace: marketplace, uid: user_uid)
# Remove the user
organization.remove_user(user)
end
end |
...
The code below shows how to bill an organization which has been tagged with a tenant keymarketplace key. All Maestrano SDKs offer the ability to scope REST calls with a tenant keymarketplace key. See the documentation of the relevant SDK for more details.
Code Block |
---|
language | ruby |
---|
title | MonthlyBillingJob |
---|
linenumbers | true |
---|
|
class MonthlyBillingJob
# Run the billing job
function run
foreach organization in Organization.all()
if organization.maestrano_tenant_keymarketplace != null
# Use Maestrano's billing API
Bill.with(organization.maestrano_tenant_keymarketplace).create(amount: $100, group_id: organization.maestrano_uid)
else
# For your own customers, just charge as usual
organization.charge_credit_card($100)
end
end
end
end |
...
Code Block |
---|
language | ruby |
---|
title | InvoiceModel |
---|
linenumbers | true |
---|
|
class InvoiceModel
function save
return false unless this.save_to_db
if this.maestrano_uid
client = MaestranoConnecClient.with(this.maestrano_tenant_keymarketplace).new(this.maestrano_group_uid)
client.post('/invoices', this.to_maestrano_json)
end
end
end |
...
Code Block |
---|
language | ruby |
---|
title | ConnecWebhookController |
---|
linenumbers | true |
---|
|
# This controller processes any data sharing notifications sent by tenantsmarketplaces via
# Connec!
# E.g.: I receive a new invoice from Connec!™ that was created in another application
class WebhookConnecController
# The 'receive' controller action responds to the following route
# POST /mno-enterprise/acme-corp/connec/receive
function receive
# Retrieve the tenant key from the URL parameters
tenant_keymarketplace = params['tenant_keymarketplace']
# Authenticate request as usual
unless Maestrano.with(tenant_keymarketplace).authenticate(http_basic['login'],http_basic['password'])
render json: "Unauthorized, code: '401'
end
# Finally, process the request for a specific tenantmarketplace
MyConnecWrapperClass.process_invoice_updates(params['invoices'],tenant_key marketplace)
end
end |
4 Good practice: handling objects connected to third parties
...