We use https://waffle.io/softlayer/sl-ember-behavior to work our issues.
An Ember CLI Addon that provides the ability to define and enforce behaviors, combining the concepts of whether a user has permission to perform an action and whether that action can currently be performed.
For example, does a user have permission to reboot a device and is that device currently able to be rebooted.
Want to restrict access to routes? It is very easy to use only the permission capabilities of the behaviors and we have included a route behavior mixin for just this purpose.
You define all possible behaviors you wish to present as possibilities in your application. Setting their values to either true or false represents whether the user has the permission to perform this behavior. You can define additional logic that should be executed to further refine whether the activity can be performed on a resource once it has been determined that the user has permission to do so. This additional logic can be contained in any object you desire, such as your models.
http://softlayer.github.io/sl-ember-behavior/#/demo
git clone
this repositorynpm install
bower install
ember server
- View the demo at http://localhost:4200
For more information on using ember-cli, visit http://www.ember-cli.com/.
ember ember-cli-jsoc
ornpm run docs
(shortcut setup in this repo)- Visit http://localhost:4200/docs
ember install sl-ember-behavior
Get a reference to the Behavior Service and pass your behaviors as the only argument to setBehaviors()
In a route, for example:
behaviorService: Ember.inject.service( 'sl-behavior' ),
beforeModel() {
this.get( 'behaviorService' ).setBehaviors( yourBehaviorData );
}
The structure of your behavior data should be as follows:
{
"comments": {
"create" : true,
"view" : true,
"edit" : false
},
"articles": {
"read" : true,
"create" : false,
"edit" : false
},
"route": {
"application" : true,
"application_loading" : true,
"error" : true,
"loading" : true,
"devices" : true,
"devices.details" : true,
"devices.error" : true,
"devices.index" : true,
"devices.loading" : true,
"users" : false,
"users.details" : false,
"users.error" : false,
"users.index" : false,
"users.loading" : false
}
}
In this example, the comments
, articles
, and routes
keys represent what is referred to internally to the Behavior Service as "Resources". Within these groups are the individual "Activities" that, in this structure, represent whether a user has permission to perform the listed action.
NOTE: Except for the route
key name it DOES NOT matter what you name the keys (Resources) as they represent whatever CONCEPTS you want them to relate to in your application.
NOTE: Except for the route
key name it DOES NOT matter what case you use for the keys (Resources) though you will need to use the same case when referencing them.
The route
key (Resource) entries are only needed if you want to restrict access to routes. If you are going to restrict route access we recommend using code such as
Object.keys( this.get( 'router.router.recognizer.names' ) ).forEach( function( route ) { ... });
from within the ApplicationRoute
in order to correctly capture all of the routes that are auto-magically generated in your application, such as error
, routename.loading
, etc. In the example given you can see these such routes represented and the user's access to them being managed. If your application does not make use of the error
and loading
routes, for example, then you would not need to populate them.
NOTE: The route
key name is important in both spelling and case, as it is used by the Route Mixin to restrict route access.
The sl-able
component is a block form component that is used to determine whether its content should be rendered.
{{#sl-able activity="setDate" resource="event"}}
This will be displayed.
So will the {{sl-calendar}} component
{{/sl-able}}
This parameter must be a string. It corresponds to the Activity key name used in the Behaviors structure previously explained.
This parameter must be a string. It corresponds to the Resource key name used in the Behaviors structure previously explained and represents a permission-only use of the data to make a determination. For example, given these Behaviors and their usage:
{
event: {
cancel : false,
setDate : true,
reschedule : false,
editDate : true
}
}
{{#sl-able activity="setDate" resource="event"}}
This will be displayed.
So will the {{sl-calendar}} component
{{/sl-able}}
the resource
and activity
values are compared to the related keys to return their associatively combined boolean value, which in this example is true
.
If the optional possible
parameter is provided, its value will be applied as additional logic beyond the initial permission check to determine whether the user is allowed to perform the provided behavior.
As already alluded to, but which will now be stated explicitly, in order for additional logic to considered in the determination of allowable behaviors, the user first must have permission for the behavior in question. This means that the Activity must be set to true
for the Resource, otherwise even if additional logic is defined it will never be considered because the user doesn't first and foremost have the correct permission.
The possible
parameter accepts a boolean value or a boolean computed property:
MyEventModel = Model.extend({
canCancel: false
});
or
MyEventModel = Model.extend({
canCancel: Ember.computed(
'anotherProperty',
function() {
return this.get( 'another' );
}
),
anotherProperty: false
});
Either of the above could be used with the following template code when eventModel
is an instance of MyEventModel
:
{{#sl-able activity="setDate" resource="event" possible=eventModel.canCancel }}
This will NOT be displayed.
{{/sl-able}}
This component is identical to the sl-able
component, except that it checks for the inverse condition.
Restricting access to a route represents a permission-only use of the Behavior data. For any route you wish to restrict access to simply mix in the Route Mixin, add the route
Behavior Group in the Behaviors data, as well as list the appropriate route name and its boolean value representing the users permission.
The Route Mixin extends beforeModel(). If a user is allowed access to the specified route then Ember is allowed to transition to the route as usual. If the user is not allowed access to the route one of two things then happen, though they both represent the idea that the user is not allowed to transition to the route.
The Route Mixin also introduces the unableRoute
property which defaults to null
. If this value remains null
and a user attempts to transition to a route they have been restricted from then the route transition is aborted through the use of transition.abort()
. If however the unableRoute
property has been populated with a valid route for your application, such as one explaining why they couldn't access the route they were attempting to, the user will instead be transitioned there.
import Ember from 'ember';
import Behavior from 'sl-ember-behavior/mixins/route';
export default Ember.Route.extend( Behavior, {
unableRoute: 'event.restricted'
});
Make sure you have read the "Components" section to understand what the activity
, resource
, and possible
parameters represent and how they are used.
To use the Behavior Service, simply inject it into a property in the class in which you will use it.
behaviorService: Ember.inject.service( 'sl-behavior' ),
The Behavior Service provides two methods, isAble()
and isUnable()
that are the methods behind the Component logic.
The simplest use of the isAble()
method is shown in the first example. The remaining two examples show the use of isAble()
with the optional possible parameter.
Example 1
if ( this.get( 'behaviorService' ).isAble( 'setData', 'event' ) ) { ... }
Example 2: Boolean value in the possible
parameter
let canSetEventData = false;
if ( this.get( 'behaviorService' ).isAble( 'setData', 'event', canSetEventData ) ) { ... }
Example 3: Using a computed property's value in the possible
parameter
canSetEventData: Ember.computed(
'isReady',
function() {
return this.get( 'isReady' ) && !myEvent.get( 'readOnly' );
}
),
isReady: false,
...
if (
this.get( 'behaviorService' ).isAble(
'setData',
'event',
this.get( 'canSetEventData' )
)
) { ... }
The isUnable()
function takes the same parameters as isAble()
and is used the same way:
Example 1
if ( this.get( 'behaviorService' ).isUnable( 'setData', 'event' ) ) { ... }
Example 2: Boolean value in the possible
parameter
let canSetEventData = false;
if ( this.get( 'behaviorService' ).isUnable( 'setData', 'event', canSetEventData ) ) { ... }
Example 3: Using a computed property's value in the possible
parameter
canSetEventData: Ember.computed(
'isReady',
function() {
return this.get( 'isReady' ) && !myEvent.get( 'readOnly' );
}
),
isReady: false,
...
if (
this.get( 'behaviorService' ).isUnable(
'setData',
'event',
this.get( 'canSetEventData' )
)
) { ... }
Employs Semantic Versioning 2.0.0
sl-ember-behavior and its source files are Copyright © 2014-2015 SoftLayer Technologies, Inc. The software is MIT Licensed
This software is provided “as is” and without any express or implied warranties, including, without limitation, the implied warranties of merchantability and fitness for a particular purpose.