Odds:

16:11:28

Tue, 20 Oct

Welcome to the Multi-tenant Proof of Concept

 

You may login as:

 

Or you may register for a new account (not permanent, accounts are erased whenever IIS clears the application pool)

 

All operations (i.e. login, registration, account edit) are handled by an external api that persists all the members. Umbraco does not handle members.

Only pages are handled by Umbraco.



Umbraco version 8.0.2

This site exposes various methods as Api for an external tenant solution to control the creation of tenants, users, pages, etc.

The Json-RPC controller allows external applications to interact with Umbraco creating nodes, groups, users, and media folders

namespace Umbraco.Plugins.Connector.Controllers
{
    using AustinHarris.JsonRpc;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using Umbraco.Plugins.Connector.Cache;
    using Umbraco.Plugins.Connector.Exceptions;
    using Umbraco.Plugins.Connector.Helpers;
    using Umbraco.Plugins.Connector.Interfaces;
    using Umbraco.Plugins.Connector.Models;
    using Umbraco.Plugins.Connector.Services;

    public class JsonRpcController : JsonRpcService, IApiConnectorJsonRpc
    {
        private static readonly Dictionary allowedApps = new Dictionary();

        [JsonRpcMethod]
        public JsonRpcResponseData AssignUserGroups(string[] groups, string username, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var assignedGroups = apiController.AssignUserGroups(groups, username);
                return new JsonRpcResponseData
                {
                    Message = "Groups assigned to user",
                    Status = JsonRpcResponseData.OK,
                    Data = new
                    {
                        Username = username,
                        assignedGroups
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData ChangePassword(TenantUser payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var username = apiController.ChangePassword(payload);
                return new JsonRpcResponseData
                {
                    Message = $"Password for {username} changed",
                    Status = JsonRpcResponseData.OK,
                    Data = new
                    {
                        Username = username,
                        payload.Password
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData CreateGroup(TenantGroup payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var groupId = apiController.CreateGroup(payload);
                return new JsonRpcResponseData
                {
                    Message = $"Group {payload.Name} Created",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = payload.TenantUid.ToString(),
                    Data = new
                    {
                        groupId
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData CreateTenant(Tenant tenant, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var extended = apiController.CreateTenant(tenant);

                return new JsonRpcResponseData
                {
                    Message = "Tenant Created",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = extended.Tenant.TenantUId.ToString(),
                    Data = new
                    {
                        extended
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData CreateUser(TenantUser payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var assignedUmbracoUserId = apiController.CreateUser(payload);
                return new JsonRpcResponseData
                {
                    Message = "User created",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = payload.TenantUId.ToString(),
                    Data = assignedUmbracoUserId
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData DisableTenant(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var tenantUid = apiController.DisableTenant(payload);
                return new JsonRpcResponseData
                {
                    Message = "Tenant disabled",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = tenantUid
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData DisableUser(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                apiController.DisableUser(payload);
                return new JsonRpcResponseData
                {
                    Message = "User disabled",
                    Status = JsonRpcResponseData.OK,
                    Data = new { payload.Username }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData EditTenant(TenantData tenant, ApiAuthorization authorization, TenantUser user = null, TenantGroup group = null)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var extended = apiController.EditTenant(tenant, user, group);
                return new JsonRpcResponseData
                {
                    Message = "Tenant edited",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = extended.Tenant.TenantUId.ToString(),
                    Data = new {
                        extended
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData EnableTenant(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var tenantUid = apiController.EnableTenant(payload);
                return new JsonRpcResponseData
                {
                    Message = "Tenant enabled",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = tenantUid
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData EnableUser(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var newPassword = apiController.EnableUser(payload);
                return new JsonRpcResponseData
                {
                    Message = $"User {payload.Username} Enabled",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = payload.TenantUId.Value.ToString()
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData LoginTenant(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                apiController.LoginTenant(payload);
                return new JsonRpcResponseData
                {
                    Message = $"Tenant Logged in",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = payload.TenantUId.Value.ToString(),
                    Data = new
                    {
                        payload.Username
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData PurgeTenant(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var purge = apiController.PurgeTenant(payload);
                return new JsonRpcResponseData
                {
                    Message = "Tenant Purged from CMS",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = payload.TenantUId.Value.ToString(),
                    Data = purge
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        public JsonRpcResponseData ResetPassword(SimpleTenant payload, ApiAuthorization authorization)
        {
            SetupAuth();
            IsValidRequest(authorization.AppId, authorization.ApiKey);
            var apiController = new ControllerService();
            try
            {
                var newPassword = apiController.ResetPassword(payload);
                return new JsonRpcResponseData
                {
                    Message = $"Password for {payload.Username} Reset",
                    Status = JsonRpcResponseData.OK,
                    TenantUid = payload.TenantUId.Value.ToString(),
                    Data = new
                    {
                        newPassword
                    }
                };
            }
            catch (System.Exception ex)
            {
                throw HandleException(ex);
            }
        }

        [JsonRpcMethod]
        private string Ping(string message)
        {
            return $"Ping received: {message}";
        }

        #region Authentication Helpers
        private void IsValidRequest(string appId, string apiKey)
        {
            var sharedKey = allowedApps[appId];
            if (!EncryptDecryptHelper.Sha256Matches(apiKey, sharedKey))
            {
                throw new JsonRpcException(ExceptionCode.NotAuthorized.GetHashCode(), "Not authenticated", new { appId, apiKey });
            }
        }

        private void SetupAuth()
        {
            var keys = ApiKeyCache.Keys.ToList();
            if (keys.Count > 0)
            {
                foreach (var key in keys)
                {
                    if (!allowedApps.ContainsKey(key.AppId))
                        allowedApps.Add(key.AppId, key.ApiKey);
                }
            }
            // add Total Code Admin App Id and Api Key
            if (!allowedApps.ContainsKey(ConfigurationManager.AppSettings["TotalCode.Admin.AppId"]))
                allowedApps.Add(ConfigurationManager.AppSettings["TotalCode.Admin.AppId"], ConfigurationManager.AppSettings["TotalCode.Admin.ApiKey"]);
        }
        #endregion

        #region Exception Handling
        private JsonRpcException HandleException(System.Exception ex)
        {
            if (ex.GetType().Equals(typeof(TenantException)))
            {
                var exception = (TenantException)ex;
                return new JsonRpcException(exception.Code.GetHashCode(), exception.Message, new { exception.TenantUid, exception.Info });
            }
            else
            {
                return new JsonRpcException(ExceptionCode.Unhandled.GetHashCode(), ex.Message, new { ex.InnerException, ex.StackTrace });
            }
        }
        #endregion
    }
}