Declarative Access Control in WebDSL
The Access Control sublanguage is supported by a session entity that holds information
regarding the currently logged in user. This session entity is called the securityContext
and is configured as follows: This states that the User entity will be the entity representing a logged in user.
The credentials are not used in the current implementation (the idea is to derive a default
login template). The resulting generated session entity will be: Note that this principal declaration is used to enable the entire Access Control sublanguage. Authentication must be added manually for now. Here are templates for login and logout that you can
include in pages: When storing a secret property you need to create a digest of it: This makes sure the secret property is stored encrypted.
A digest can be compared with an entered string using the check method: The default policy is to deny access to all pages and actions, the rules will
determine what the conditions for allowing access are. A simple rule protecting the editUser page to be only accessable by the user
being edited looks like this: An analysis of this rule: Matching can be done a bit more freely using a trailing * as
wildcard character, both in resource name and arguments: When more fine-grained control is needed for rules, it is possible to specify
nested rules. This implies that the nested rule is only valid for usage of that
resource inside the parent resource. The allowed combinations are page - action,
template - action, page - template. The next example shows nested rules for actions in a page: This flexibility is often not necessary, and it is also inconvenient
having to explicitly allow all the actions on the page, for these reasons some
extra desugaring rules were added. When specifying a check on a page or template without
nested checks, a generic action rules block with the same check is added to it by
default. For example: becomes Predicates are functions consisting of one boolean
expression, which allows reusing complicated expressions, or simply giving
better structure to the policy implementation. An example of a predicate: Pointcuts are groups of resources to which conditions can be specified at once.
Especially the open parts of the web application are easy to handle with
pointcuts, an example: Pointcuts can also be used with parameters: A disabled page or action redirects to a very simple page stating access denied.
Since this is not very user friendly, the visibility of navigate links and action
buttons/links are automatically made conditional using the same check as the corresponding
resource. An example conditional navigate: When using conditional forms it is often more convenient to put the form in a template,
and control the visibility by a rule on the template. Access Control policies that rely on extra data can create new or extend existing properties.
An example of extending an entity is adding a set of users property to a document representing
the users allowed access to that document: Administration of Access Control in WebDSL is done by the normal WebDSL page definitions.
All the data of the Access Control policy is integrated into the WebDSL
application. An option is to incorporate the administration into an existing page with a
template. This example illustrates the use of a template for administration: The template call for this template is added to the editDocument page: By using a template the Access Control can be disabled easily by not
including the access control definitions and the template. The unresolved template definitions will give a warning but the page will generate normally and ignore the template call.Configuration of the Principal
access control rules
{
principal is User with credentials name, password
}
session securityContext
{
principal -> User
loggedIn :: Bool
}
Authentication
entity User
{
name :: String
password :: Secret
}
define login(){
var usr : User := User{};
form{
table{
row{ "Name: " input(usr.name) }
row{ "Password: " input(usr.password) }
row{ captcha() }
row{ action("Log In", login()) "" }
}
action login(){
var users : List<User> :=
select u from User as u
where (u._name = ~usr.name);
for (us : User in users ){
if (us.password.check(usr.password)){
securityContext.principal := us;
securityContext.loggedIn := true;
return viewUser(securityContext.principal);
}
}
securityContext.loggedIn := false;
return home();
}
}
}
define logout(){
"Logged in as " output(securityContext.principal)
form{
actionLink("Log Out", logout())
action logout(){
securityContext.loggedIn := false;
securityContext.principal := null;
return home();
}
}
}
newUser.password := newUser.password.digest();
us.password.check(enteredpassword)
Protecting Resources
rules page editUser(u:User){
u = securityContext.principal
}
rules page viewUs*(*){
true
}
rules page editDocument(d:Document){
d.author = securityContext.principal
rules action save(){
d.author = securityContext.principal
}
rules action cancel(){
d.author = securityContext.principal
}
}
rules page editDocument(d:Document){
d.author = securityContext.principal
}
rules page editDocument(d:Document){
d.author = securityContext.principal
rules action *(*)
{
d.author = securityContext.principal
}
}
Reuse in Access Control rules
predicate mayViewDocument (u:User, d:Document){
d.author = securityContext.principal
|| u in d.allowedUsers
}
rules page viewDocument(d:Document){
mayViewDocument(securityContext.principal,d)
}
rules page showDocument(d:Document){
mayViewDocument(securityContext.principal,d)
}
pointcut openSections(){
page home(),
page createDocument(),
page createUser(),
page viewUser(*)
}
rules pointcut openSections(){
true
}
pointcut ownUserSections(u:User){
page showUser(u),
page viewUser(u),
page someUserTask(u,*)
}
rules pointcut ownUserSections(u:User){
u = securityContext.principal
}
Inferring Visibility
if(mayViewDocument(securityContext.principal,d)){
navigate(viewDocument(d)){ "view " output(d.title) }
}
Using Entities
extend entity Document{
allowedUsers -> Set<User>
}
Administration of Access Control
define allowedUsersRow(document:Document){
row{ "Allowed Users:" input(document.allowedUsers) }
}
table{
row{ "Title:" input(document.title) }
row{ "Text:" input(document.text) }
row{ "Author:" input(document.author) }
allowedUsersRow(document)
}
Contributions by DannyGroenewegen