Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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
languageruby
titleApp Initializer
linenumberstrue
# 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
languageruby
titleApp Routes
linenumberstrue
#
# 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"
 

...

languageruby
titleMetadataController
linenumberstrue

...

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
languageruby
titleApp Initializer
linenumberstrue
# 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
languageruby
titleApp Routes
linenumberstrue
#
# 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
titleOpenID 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
languageruby
titleSamlSSOController
linenumberstrue
#
# 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
languageruby
titleWebhookAccountController
linenumberstrue
#
# 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
languageruby
titleMonthlyBillingJob
linenumberstrue
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
languageruby
titleInvoiceModel
linenumberstrue
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
languageruby
titleConnecWebhookController
linenumberstrue
# 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

...