<!DOCTYPE html>
<html lang="en">
<head>
    
    <meta charset="utf-8">
    <title>api/fluro.access.js - Documentation</title>
    
    
    <script src="scripts/prettify/prettify.js"></script>
    <script src="scripts/prettify/lang-css.js"></script>
    <!--[if lt IE 9]>
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <link type="text/css" rel="stylesheet" href="styles/prettify.css">
    <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>

<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
  <div class="navicon"></div>
</label>

<label for="nav-trigger" class="overlay"></label>

<nav >
    
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="FluroAccess.html">FluroAccess</a><ul class='methods'><li data-type='method'><a href="FluroAccess.html#.retrieveCurrentSession">retrieveCurrentSession</a></li><li data-type='method'><a href="FluroAccess.html#.setDefaultApplication">setDefaultApplication</a></li></ul></li><li><a href="FluroAPI.html">FluroAPI</a></li><li><a href="FluroAsset.html">FluroAsset</a><ul class='methods'><li data-type='method'><a href="FluroAsset.html#.avatarUrl">avatarUrl</a></li><li data-type='method'><a href="FluroAsset.html#.downloadUrl">downloadUrl</a></li><li data-type='method'><a href="FluroAsset.html#.filesize">filesize</a></li><li data-type='method'><a href="FluroAsset.html#.getUrl">getUrl</a></li><li data-type='method'><a href="FluroAsset.html#.imageUrl">imageUrl</a></li><li data-type='method'><a href="FluroAsset.html#.posterUrl">posterUrl</a></li></ul></li><li><a href="FluroAuth.html">FluroAuth</a><ul class='methods'><li data-type='method'><a href="FluroAuth.html#.addEventListener">addEventListener</a></li><li data-type='method'><a href="FluroAuth.html#.changeAccount">changeAccount</a></li><li data-type='method'><a href="FluroAuth.html#.getCurrentToken">getCurrentToken</a></li><li data-type='method'><a href="FluroAuth.html#.getCurrentUser">getCurrentUser</a></li><li data-type='method'><a href="FluroAuth.html#.login">login</a></li><li data-type='method'><a href="FluroAuth.html#.logout">logout</a></li><li data-type='method'><a href="FluroAuth.html#.refreshAccessToken">refreshAccessToken</a></li><li data-type='method'><a href="FluroAuth.html#.removeAllListeners">removeAllListeners</a></li><li data-type='method'><a href="FluroAuth.html#.removeEventListener">removeEventListener</a></li><li data-type='method'><a href="FluroAuth.html#.sendResetPasswordRequest">sendResetPasswordRequest</a></li><li data-type='method'><a href="FluroAuth.html#.set">set</a></li><li data-type='method'><a href="FluroAuth.html#.signup">signup</a></li></ul></li><li><a href="FluroCache.html">FluroCache</a><ul class='methods'><li data-type='method'><a href="FluroCache.html#.get">get</a></li><li data-type='method'><a href="FluroCache.html#.reset">reset</a></li></ul></li><li><a href="FluroContent.html">FluroContent</a><ul class='methods'><li data-type='method'><a href="FluroContent.html#.external">external</a></li><li data-type='method'><a href="FluroContent.html#.get">get</a></li><li data-type='method'><a href="FluroContent.html#.query">query</a></li><li data-type='method'><a href="FluroContent.html#.retrieve">retrieve</a></li><li data-type='method'><a href="FluroContent.html#.search">search</a></li><li data-type='method'><a href="FluroContent.html#.slug">slug</a></li></ul></li><li><a href="FluroCore.html">FluroCore</a></li><li><a href="FluroDate.html">FluroDate</a><ul class='methods'><li data-type='method'><a href="FluroDate.html#.localDate">localDate</a></li><li data-type='method'><a href="FluroDate.html#.readableEventDate">readableEventDate</a></li></ul></li><li><a href="FluroTypes.html">FluroTypes</a><ul class='methods'><li data-type='method'><a href="FluroTypes.html#.retrieve">retrieve</a></li></ul></li><li><a href="FluroUtils.html">FluroUtils</a><ul class='methods'><li data-type='method'><a href="FluroUtils.html#.arrayIDs">arrayIDs</a></li><li data-type='method'><a href="FluroUtils.html#.comma">comma</a></li><li data-type='method'><a href="FluroUtils.html#.errorMessage">errorMessage</a></li><li data-type='method'><a href="FluroUtils.html#.getStringID">getStringID</a></li><li data-type='method'><a href="FluroUtils.html#.mapParameters">mapParameters</a></li></ul></li></ul><h3>Modules</h3><ul><li><a href="module-fluro.html">fluro</a></li></ul>
</nav>

<div id="main">
    
    <h1 class="page-title">api/fluro.access.js</h1>
    

    



    
    <section>
        <article>
            <pre class="prettyprint source linenums"><code>import _ from 'lodash';
import { EventDispatcher } from './fluro.utils';


///////////////////////////////////////////////////////////////////////////////

/**
 * Creates a new FluroAccess service
 * This module provides helpful functions and tools for managing and understanding a user's permissions and access control
 * 
 * @constructor
 * @param {FluroCore} fluro A reference to the parent instance of the FluroCore module. This module is usually created by a FluroCore instance that passes itself in as the first argument.
 */
var FluroAccess = function(FluroCore) {


    if (!FluroCore.auth) {
        throw new Error(`Can't Instantiate FluroAccess before FluroAuth has been initialized`);
    }

    //////////////////////////////////

    var store = {};
    var service = {};

    //Create a new dispatcher
    var dispatcher = new EventDispatcher();
    dispatcher.bootstrap(service);


    ///////////////////////////////////////////////////

    /**
     * 
     * Sets the default application so that if the current user is running
     * in the context of an application and not an authenticated user this
     * service can still understand and respond according to the permission sets of the 
     * application itself
     * @alias FluroAccess.setDefaultApplication
     * @param  {Object} application The application session data, usually available before this service is initialized
     * @example
     * FluroAccess.setDefaultApplication(window.applicationData._application)
     */

    service.setDefaultApplication = function(application) {
        store.application = application;
        //Dispatch an event that the application data changed
        dispatcher.dispatch('application', application);
    }

    //////////////////////////////////

    service.isFluroAdmin = function() {
        var user = FluroCore.auth.getCurrentUser();

        //If we are not authenticated as a user
        if (!user) {
            return;
        }

        //If we are not an administrator
        if (user.accountType != 'administrator') {
            return;
        }

        //If we are pretending to be someone else
        //or impersonating a persona
        if (user.pretender) {
            return;
        }

        //We are a fluro admin
        return true;
    }

    //////////////////////////////////

    /**
     * Returns either the currently logged in user, or the acting application
     * @alias FluroAccess.retrieveCurrentSession
     * @return {Object} The user or application session that is currently active
     */
    service.retrieveCurrentSession = function() {

        var user = FluroCore.auth.getCurrentUser();
        var application = store.application;

        if (user) {
            return user;
        }

        if (application) {
            return application;
        }

        return
    }

    //////////////////////////////////

    /**
     * Checks whether a user has permission to perform a specified action for a specified type of content
     * If no user is set but an application is then it will return according to the permissions of the application
     * This function is syncronous and returns a basic true or false boolean
     * @param  {String} action     The action to check permissions for eg. 'create', 'view any', 'edit own', 'delete any' etc
     * @param  {String} type       The type or definition name eg. 'photo', 'article', 'team'
     * @param  {String} parentType The basic type, for instance if the type you are checking is 'photo' the parent type would be 'image' so that
     * you can get an accurate return value if the user has permission to perform the action on all definitions of an 'image' type content item 
     * @return {Boolean}            true or false depending on whether the user has the required permissions
     */
    service.can = function(action, type, parentType) {

        //Get the current session
        var session = service.retrieveCurrentSession();

        //If we are not logged in and are not
        //running as an application then we can't
        //do anything
        if (!session) {
            return false;
        }

        //If we are an administrator
        //then we have access to do everything
        //so there is no point continuing with checking all the other criteria
        if (service.isFluroAdmin()) {
            return true;
        }

        /////////////////////////////////////////////////////

        //Get the permission string we actually want to check against
        var permissionString = `${action} ${type}`;

        /////////////////////////////////////////////////////

        //Track the realms we are allowed to do this in
        var realms = [];

        /////////////////////////////////////////////////////

        //Check if we can do this permission in any realms
        switch (action) {
            case 'view any':
                var canViewAnyRealms = service.retrieveActionableRealms('view any ' + type);
                var canEditAnyRealms = service.retrieveActionableRealms('edit any ' + type);

                //Combine the realms
                realms = realms.concat(canViewAnyRealms);
                realms = realms.concat(canEditAnyRealms);
                break;
            case 'view own':
                var canViewOwnRealms = service.retrieveActionableRealms('view own ' + type);
                var canEditOwnRealms = service.retrieveActionableRealms('edit own ' + type);

                //Combine the realms
                realms = realms.concat(canViewOwnRealms);
                realms = realms.concat(canEditOwnRealms);
                break;
            default:
                realms = service.retrieveActionableRealms(permissionString);
                break;
        }

        /////////////////////////////////////////////////////

        //If there are realms that we can do this in
        //then we can return true here
        if (realms.length) {
            return true;
        }

        /////////////////////////////////////////////////////

        //Check if the user has any permissions on the parent type that would allow them to perform the action
        if (parentType &amp;&amp; parentType.length) {

            //Check if we have flowdown from the parent type
            var includeDefined = service.retrieveActionableRealms('include defined ' + parentType);

            //If not there is no point continuing with the check
            if (!includeDefined.length) {
                return false;
            }

            //If so we now need to check if we can perform
            //the action on the parent in any realms
            switch (action) {
                case 'view any':
                    var canViewAnyParentRealms = service.retrieveActionableRealms('view any ' + parentType);
                    var canEditAnyParentRealms = service.retrieveActionableRealms('edit any ' + parentType);

                    //Combine the realms
                    realms = realms.concat(canViewAnyRealms);
                    realms = realms.concat(canEditAnyRealms);
                    break;
                case 'view own':
                    var canViewOwnParentRealms = service.retrieveActionableRealms('view own ' + parentType);
                    var canEditOwnParentRealms = service.retrieveActionableRealms('edit own ' + parentType);

                    //Combine the realms
                    realms = realms.concat(canViewOwnParentRealms);
                    realms = realms.concat(canEditOwnParentRealms);
                    break;
                default:
                    realms = service.retrieveActionableRealms(action + ' ' + parentType);
                    break;
            }

            if (realms.length) {
                ////////console.log('Return true because of parent permissions')
                return true;
            }
        }

        //Nope we cant
        return false;

    }

    /////////////////////////////////////////////////////

    //Flatten all children for a specified permission set
    //so you get a flat array of included realm ids
    //this function is recursive and will include all sub realms
    function retrieveKeys(set, additional) {
        if (set.children &amp;&amp; set.children.length) {
            _.each(set.children, function(child) {
                retrieveKeys(child, additional);
            })
        }

        additional.push(String(set._id));
    }

    //////////////////////////////////

    /**
     * Retrieves all realms the acting user or application can perform an action in
     * @param  {String} permission The permission string to retrieve realms for
     * @return {Array}        An array of realms that the user can perform the action in
     */
    service.retrieveActionableRealms = function(action) {

        //Get the current acting user session
        var session = service.retrieveCurrentSession();

        //No session so can't perform any actions
        //in any realms
        if (!session) {
            return [];
        }

        /////////////////////////////////

        //Get the permission sets
        var permissionSets = session.permissionSets;


        //Find all realms that the current acting session
        //can perform the specified action in
        var realms = _.chain(permissionSets)
            .map(function(realmSet, key) {

                //Does the set include this permission
                var hasPermission = _.includes(realmSet.permissions, action);

                if (hasPermission) {
                    var keys = [];
                    retrieveKeys(realmSet, keys);
                    return keys;
                }
            })
            .flatten()
            .compact()
            .value();

        /////////////////////////////////

        return realms;
    }



    ///////////////////////////////////////////////////////////////////////////////

    /**
     * Check whether a user has a specific permission, useful for checking custom permissions
     * or simply whether or not a user has a permission in any realm
     * @param  {String}  permission The permission to check
     * @return {Boolean}            
     */
    service.has = function(permission) {

        //Get the current acting user session
        var user = service.retrieveCurrentSession();

        if (!user) {
            return false;
        }

        if (service.isFluroAdmin()) {
            return true;
        }

        ///////////////////////////////////////////////////////////////////////////////

        var permissionSets = user.permissionSets;

        //Get all of the possible permissions
        var permissions = _.chain(permissionSets)
            .reduce(function(results, set, key) {

                results.push(set.permissions);

                return results;
            }, [])
            // .map(retrieveSubRealms)
            .flattenDeep()
            .compact()
            .uniq()
            .value();

        //Check if any of the users permissions include the one
        //we are looking for
        return _.includes(permissions, permission);
    }

    /////////////////////////////////////////////////////
    /**
     * Checks whether the currently authenticated user is the author or owner of a specified
     * content item
     * @param  {Object}  item The item to check if the user is an author of
     * @return {Boolean}      
     */
    service.isAuthor = function(item) {

        //Get the current acting user session
        var user = service.retrieveCurrentSession();

        if (!user) {
            return false;
        }

        if (!item) {
            return false;
        }



        ////////////////////////////////////////

        var userID = FluroCore.utils.getStringID(user);
        var authorID = FluroCore.utils.getStringID(item.author);

        //The user is the author if the user's id matches
        //the content author's id
        var author = userID == authorID;

        ////////////////////////////////////////

        //If we are the author at this point
        //return early
        if (author) {
            return true;
        }

        //Check if the persona matches the managed author
        var personaID = FluroCore.utils.getStringID(user.persona);
        var managedAuthorID = FluroCore.utils.getStringID(item.managedAuthor);

        //If the user's persona is the managed author of the content
        if (personaID == managedAuthorID) {
            author = true;
        }
        ////////////////////////////////////////

        //If we are the author at this point
        //return early
        if (author) {
            return true;
        }

        ////////////////////////////////////////

        //Check if the item has any owners listed on it
        var ownerIDs = FluroCore.utils.arrayIDs(item.owners);

        //If owners are listed
        if (ownerIDs &amp;&amp; ownerIDs.length) {
            //Check if the user is listed as an owner
            author = _.includes(ownerIDs, userID);
        }

        ////////////////////////////////////////

        //If we are the author at this point
        //return early
        if (author) {
            return true;
        }


        ////////////////////////////////////////

        //Check if the item has any managed owners listed on it
        var managedOwnerIDs = FluroCore.utils.arrayIDs(item.managedOwners);

        //If managed owners are listed
        if (managedOwnerIDs &amp;&amp; managedOwnerIDs.length) {
            //Check if the user is listed as an owner
            author = _.includes(managedOwnerIDs, personaID);
        }

        ////////////////////////////////////////

        var itemID = FluroCore.utils.getStringID(item);

        //If the user is trying to edit their own user
        if (userID == itemID) {
            author = true;
        }

        //If the user is trying to edit their own persona
        if (personaID == itemID) {
            author = true;
        }

        return author;
    }


    /////////////////////////////////////////////////////

    /**
     * Check whether the current acting user can edit a specified content item
     * @param  {Object} item The item to check if the user can edit
     * @return {Boolean}    
     */
    service.canEditItem = function(item, isUser) {

        if (!item) {
            return false;
        }

        //Get the current acting user or application
        var user = service.retrieveCurrentSession();

        if (!user) {
            return false;
        }

        //Store the itemID in case we need to reference it below
        var itemID = FluroCore.utils.getStringID(item);

        /////////////////////////////////////

        //Check the account of the user
        //and the account of the content
        var userAccountID = FluroCore.utils.getStringID(user.account);
        var contentAccountID = FluroCore.utils.getStringID(item.account);

        //If there is an account listed on the content and it does not
        //match the account of the user then we can't edit it
        if (contentAccountID &amp;&amp; (contentAccountID != userAccountID)) {
            return false;
        }

        /////////////////////////////////////

        //If we are a Fluro Admin we can do anything!
        if (service.isFluroAdmin()) {
            return true;
        }

        /////////////////////////////////////

        //Get the definition name of the item
        //we are trying to edit
        var definitionName = item._type;
        var parentType;

        //If the item is a defined type
        //store the definition and the parent type
        if (item.definition) {
            definitionName = item.definition;
            parentType = item._type;
        }

        ////////////////////////////////////////

        //Check if the user is the author of this content
        var author = service.isAuthor(item);

        //If the content we are checking is a Fluro User
        //We used to allow the user to edit their own user
        //but we don't allow this anymore
        //user profile
        // if (isUser) {
        //     definitionName = 'user';
        //     if (author) {
        //         return true;
        //     }
        // }

        ////////////////////////////////////////

        //Find the realms we are allowed to edit this kind of content in
        var editAnyRealms = service.retrieveActionableRealms('edit any ' + definitionName);
        var editOwnRealms = service.retrieveActionableRealms('edit own ' + definitionName);

        ////////////////////////////////////////

        //Keep track of the realms of the content
        var contentRealmIDs;

        //If we are checking a realm then we need to check the trail
        //instead of the 'item.realms' array
        if (definitionName == 'realm' || parentType == 'realm') {

            //Check the realm.trail
            contentRealmIDs = FluroCore.utils.arrayIDs(item.trail);

            //Include the realm itself
            contentRealmIDs.push(itemID);
        } else {

            //Retrieve all the realms the content is currently in
            contentRealmIDs = FluroCore.utils.arrayIDs(item.realms);
        }

        ////////////////////////////////////////

        //Check if the user has any permissions on the parent type that will allow them to access this content
        if (parentType &amp;&amp; parentType.length) {

            var includeDefined = service.retrieveActionableRealms('include defined ' + parentType);

            //If we can adjust the parent and it's defined child types in any realms
            if (includeDefined.length) {

                var canEditAnyParentRealms = service.retrieveActionableRealms('edit any ' + parentType);
                editAnyRealms = editAnyRealms.concat(canEditAnyParentRealms);

                var canEditOwnParentRealms = service.retrieveActionableRealms('edit own ' + parentType);
                editOwnRealms = editOwnRealms.concat(canEditOwnParentRealms);
            }
        }

        ////////////////////////////////////////

        //Find realms the content is in that we are allowed to edit within
        var matchedAnyRealms = _.intersection(editAnyRealms, contentRealmIDs);

        //We are allowed to edit anything in these realms
        //So return true
        if (matchedAnyRealms.length) {
            return true;
        }

        ////////////////////////////////////////

        //If we are the author of the content
        if (author) {

            //Find own matches between this content
            var matchedOwnRealms = _.intersection(editOwnRealms, contentRealmIDs);

            //We are allowed to edit anything in these realms
            //So return true
            if (matchedOwnRealms.length) {
                return true;
            }
        }

        return false;
    }

    /////////////////////////////////////////////////////

    /**
     * Check whether the current acting user can view a specified content item
     * @param  {Object} item The item to check if the user can view
     * @return {Boolean}    
     */
    service.canViewItem = function(item, isUser) {

        if (!item) {
            return false;
        }

        //Get the current acting user or application
        var user = service.retrieveCurrentSession();

        if (!user) {
            return false;
        }

        /////////////////////////////////////

        //If we are a Fluro Admin we can do anything!
        if (service.isFluroAdmin()) {
            return true;
        }

        /////////////////////////////////////

        //Store the itemID in case we need to reference it below
        var itemID = FluroCore.utils.getStringID(item);

        /////////////////////////////////////

        var definitionName = item._type;
        var parentType

        if (item.definition) {
            definitionName = item.definition;
            parentType = item._type;
        }

        ////////////////////////////////////////
        //Check if the user is the author of this content
        var author = service.isAuthor(item);

        // if (isUser) {
        //     definitionName = 'user';

        //     if (author) {
        //         return true;
        //     }
        // }

        ////////////////////////////////////////

        //Get the realms we are allowed to work in
        var viewAnyRealms = service.retrieveActionableRealms('view any ' + definitionName);
        var viewOwnRealms = service.retrieveActionableRealms('view own ' + definitionName);
        var editAnyRealms = service.retrieveActionableRealms('edit any ' + definitionName);
        var editOwnRealms = service.retrieveActionableRealms('edit own ' + definitionName);

        //Combine any
        var combinedAnyRealms = [];
        combinedAnyRealms = combinedAnyRealms.concat(viewAnyRealms);
        combinedAnyRealms = combinedAnyRealms.concat(editAnyRealms);

        //Combine own
        var combinedOwnRealms = [];
        combinedOwnRealms = combinedOwnRealms.concat(viewOwnRealms);
        combinedOwnRealms = combinedOwnRealms.concat(editOwnRealms);

        ////////////////////////////////////////

        //Keep track of the realms of the content
        var contentRealmIDs;

        //If we are checking a realm then we need to check the trail
        //instead of the 'item.realms' array
        if (definitionName == 'realm' || parentType == 'realm') {

            //Check the realm.trail
            contentRealmIDs = FluroCore.utils.arrayIDs(item.trail);

            //Include the realm itself
            contentRealmIDs.push(itemID);
        } else {

            //Retrieve all the realms the content is currently in
            contentRealmIDs = FluroCore.utils.arrayIDs(item.realms);
        }

        ////////////////////////////////////////

        //Check if the user has any permissions on the parent type that will allow them to access this content
        if (parentType &amp;&amp; parentType.length) {
            var includeDefined = service.retrieveActionableRealms('include defined ' + parentType);

            if (includeDefined.length) {
                var canEditAnyParentRealms = service.retrieveActionableRealms('edit any ' + parentType);
                var canViewAnyParentRealms = service.retrieveActionableRealms('view any ' + parentType);
                combinedAnyRealms = combinedAnyRealms.concat(canEditAnyParentRealms, canViewAnyParentRealms);

                var canEditOwnParentRealms = service.retrieveActionableRealms('edit own ' + parentType);
                var canViewOwnParentRealms = service.retrieveActionableRealms('view own ' + parentType);
                combinedOwnRealms = combinedOwnRealms.concat(canEditOwnParentRealms, canViewOwnParentRealms);
            }
        }

        ////////////////////////////////////////

        //Find any matches between this content
        var matchedAnyRealms = _.intersection(combinedAnyRealms, contentRealmIDs);

        //We are allowed to view anything in these realms
        //So return true
        if (matchedAnyRealms.length) {
            return true;
        }

        ////////////////////////////////////////

        //If we are the author
        if (author) {
            //Find own matches between this content
            var matchedOwnRealms = _.intersection(combinedOwnRealms, contentRealmIDs);

            //We are allowed to view anything in these realms
            //So return true
            if (matchedOwnRealms.length) {
                return true;
            }
        }

        return false;

    }


    /////////////////////////////////////////////////////

    /**
     * Check whether the current acting user can delete a specified content item
     * @param  {Object} item The item to check if the user can delete
     * @return {Boolean}    
     */
    service.canDeleteItem = function(item, isUser) {

        if (!item) {
            return false;
        }

        //Get the current acting user or application
        var user = service.retrieveCurrentSession();

        if (!user) {
            return false;
        }

        //Store the itemID in case we need to reference it below
        var itemID = FluroCore.utils.getStringID(item);

        /////////////////////////////////////

        //Check the account of the user
        //and the account of the content
        var userAccountID = FluroCore.utils.getStringID(user.account);
        var contentAccountID = FluroCore.utils.getStringID(item.account);

        //If there is an account listed on the content and it does not
        //match the account of the user then we can't delete it
        if (contentAccountID &amp;&amp; (contentAccountID != userAccountID)) {
            return false;
        }

        /////////////////////////////////////

        //If we are a Fluro Admin we can do anything!
        if (service.isFluroAdmin()) {
            return true;
        }

        /////////////////////////////////////

        //Get the definition name of the item
        //we are trying to delete
        var definitionName = item._type;
        var parentType;

        //If the item is a defined type
        //store the definition and the parent type
        if (item.definition) {
            definitionName = item.definition;
            parentType = item._type;
        }

        ////////////////////////////////////////

        //Check if the user is the author of this content
        var author = service.isAuthor(item);

        //If the content we are checking is a Fluro User
        //We used to allow the user to delete their own user
        //but we don't allow this anymore
        //user profile
        // if (isUser) {
        //     definitionName = 'user';
        //     if (author) {
        //         return true;
        //     }
        // }

        ////////////////////////////////////////

        //Find the realms we are allowed to delete this kind of content in
        var deleteAnyRealms = service.retrieveActionableRealms('delete any ' + definitionName);
        var deleteOwnRealms = service.retrieveActionableRealms('delete own ' + definitionName);

        ////////////////////////////////////////

        //Keep track of the realms of the content
        var contentRealmIDs;

        //If we are checking a realm then we need to check the trail
        //instead of the 'item.realms' array
        if (definitionName == 'realm' || parentType == 'realm') {

            //Check the realm.trail
            contentRealmIDs = FluroCore.utils.arrayIDs(item.trail);

            //Include the realm itself
            contentRealmIDs.push(itemID);
        } else {

            //Retrieve all the realms the content is currently in
            contentRealmIDs = FluroCore.utils.arrayIDs(item.realms);
        }

        ////////////////////////////////////////

        //Check if the user has any permissions on the parent type that will allow them to access this content
        if (parentType &amp;&amp; parentType.length) {

            var includeDefined = service.retrieveActionableRealms('include defined ' + parentType);

            //If we can adjust the parent and it's defined child types in any realms
            if (includeDefined.length) {

                var canEditAnyParentRealms = service.retrieveActionableRealms('delete any ' + parentType);
                deleteAnyRealms = deleteAnyRealms.concat(canEditAnyParentRealms);

                var canEditOwnParentRealms = service.retrieveActionableRealms('delete own ' + parentType);
                deleteOwnRealms = deleteOwnRealms.concat(canEditOwnParentRealms);
            }
        }

        ////////////////////////////////////////

        //Find realms the content is in that we are allowed to delete within
        var matchedAnyRealms = _.intersection(deleteAnyRealms, contentRealmIDs);

        //We are allowed to delete anything in these realms
        //So return true
        if (matchedAnyRealms.length) {
            return true;
        }

        ////////////////////////////////////////

        //If we are the author of the content
        if (author) {

            //Find own matches between this content
            var matchedOwnRealms = _.intersection(deleteOwnRealms, contentRealmIDs);

            //We are allowed to delete anything in these realms
            //So return true
            if (matchedOwnRealms.length) {
                return true;
            }
        }

        return false;
    }

    /////////////////////////////////////////////////////

    service.retrieveSelectableRealms = function(action, type, parentType, noCache) {

        return new Promise(function(resolve, reject) {




            //Get the current acting user or application
            var user = service.retrieveCurrentSession();

            if (!user) {
                 resolve([]);
                 return;
            }

            //If we are a super user
            if (service.isFluroAdmin()) {

                //This returns the full list of all realms in a proper tree structure
                FluroCore.api.get('/realm/tree').then(function(res) {
                	return resolve(res.data)
                }, reject);
                return;
            }

            ////////////////////////////////////////////////////

            //Get the permission sets of the user
            //and then map the structure
            var permissionSets = user.permissionSets;

            //Permission String to search for
            var searchString = `${action} ${type}`;


            /////////////////////////////////////////////////////

            //Flatten all children for a specified permission set
            //so you a flat array of realm ids that are included
            function retrieveSubRealms(set) {

                var results = [set];

                if (set.children &amp;&amp; set.children.length) {
                    _.each(set.children, function(child) {
                        var additional = retrieveSubRealms(child);
                        results = results.concat(additional);
                    })

                }
                return results;
            }

            ////////////////////////////////////////////////////

            //Find all realms on the top level that we have the requested permission
            //in and then get all child realms and flatten the list, this will give us
            //all the realms that we can do the action in.
            var selectableRealms = _.chain(permissionSets)
                .filter(function(realmSet, key) {

                    //Find all permission sets where the user has the requested permission
                    var includesType = _.includes(realmSet.permissions, searchString);
                    var includedFromParent;

                    /**/
                    //If the parent type was provided also then check any sub definitions
                    //of the basic type
                    if (parentType &amp;&amp; parentType.length) {

                        //Check if we can action the parent type
                        var includesParent = _.includes(realmSet.permissions, action + ' ' + parentType);

                        //Check if we can action variants of the parent type
                        var includesVariations = _.includes(realmSet.permissions, 'include defined ' + parentType);

                        //Include this realm if both of the above return true
                        includedFromParent = (includesParent &amp;&amp; includesVariations);
                    }

                    var shouldInclude = (includesType || includedFromParent)
                    return shouldInclude;
                })
                //Recursively get all the child realms
                .map(retrieveSubRealms)
                .flattenDeep()
                .map(function(realm) {
                    return _.pick(realm, [
                        'title',
                        'definition',
                        '_discriminator',
                        '_discriminatorType',
                        'trail',
                        'color',
                        'bgColor',
                        '_id',
                    ])
                })
                .uniq(function(realm) {
                    return realm._id
                })
                .orderBy(function(realm) {
                    if (realm.trail) {
                        return realm.trail.length;
                    } else {
                        return 0;
                    }
                })
                .reduce(function(set, realm) {

                    var lastRealmParent = _.last(realm.trail);

                    if (set[lastRealmParent]) {
                        if (!set[lastRealmParent].children) {
                            set[lastRealmParent].children = [];
                        }
                        set[lastRealmParent].children.push(realm);
                        realm.nested = true;
                    }

                    set[realm._id] = realm;


                    return set;
                }, {})
                .values()
                .filter(function(realm) {
                    return !realm.nested;
                })
                .orderBy(function(realm) {
                    return realm.title;
                })
                .value();

            /////////////////////////////////////

            //Create a copy of the realm so we aren't mucking around with original user object
            // var realmTree = angular.copy(selectableRealms);



            //Resolve with our tree
            return resolve(selectableRealms);

        })

    }




    //////////////////////////////////

    return service;

}

///////////////////////////////////////////////////////////////////////////////



export default FluroAccess;</code></pre>
        </article>
    </section>




    
    
</div>

<br class="clear">

<footer>
    Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Wed Apr 24 2019 19:46:45 GMT+1000 (Australian Eastern Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>

<script>prettyPrint();</script>
<script src="scripts/polyfill.js"></script>
<script src="scripts/linenumber.js"></script>



</body>
</html>