Skip to content

Commit

Permalink
HAWKULAR-61 - Added support for roles, operations and permissions.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpkrohling committed Apr 14, 2015
1 parent 35ecba5 commit 438e877
Show file tree
Hide file tree
Showing 75 changed files with 3,614 additions and 565 deletions.
72 changes: 59 additions & 13 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The project is divided into several modules. More information about the individu
module's README files.

* accounts: the REST backend, handling the actions from the Hawkular Accounts UI.
* api: the components that are supposed to be consumed by other Hawkular components.
* api: the components that are intended to be consumed by other Hawkular components.
* distribution: assembles a complete Wildfly with Keycloak and Hawkular Accounts, suitable for isolated
development/testing.
* sample: a sample on how to integrate Accounts into other Hawkular projects.
Expand All @@ -44,26 +44,32 @@ managed beans.
Hawkular Accounts has the following concepts:

* Resource: is something that you want to protect. Might be an Alert, an Inventory item, a Metric, ...
* User: a real user of the application.
* Organization: a non-user which has an owner and a set of members.
* Persona: either an user or organization. A real user can be impersonating an organization, so, this organization
would be the current persona. In most of the cases, this is the correct representation of "current user" or "current
tenant".
* User: a real user of the application. In most cases, consumers of Hawkular Accounts don't need to deal directly
with users.
* Organization: a non-user which has an owner and a set of members. In most cases, consumers of Hawkular Accounts
don't need to deal directly with organizations.
* Owner: owns a Resource, and can be (currently) either a Hawkular User or an Organization.
* Member: an user or organization that can be part of an Organization.

To retrieve the current user:
To retrieve the current persona:
[source,java]
----
@Stateless
class FooService {
@Inject
HawkularUser user;
Persona persona;
}
----

In order to check if an user has access to a given resource, a record of this resource should exist on Hawkular
In order to check if a persona has access to a given resource, a record of this resource should exist on Hawkular
Accounts. For instance, if you need to protect a "Metric", you'll need to:

* Create a resource with a unique key (possibly the same ID you have on your persistent storage), specifying the owner
* Later on, check if the "current user" (or whichever user you'd want) has access to the resource, via
* Create a resource with a unique key (possibly the same ID you have on your persistent storage), specifying the
owner (usually, the current persona).
* Later on, check if the "current persona" (or whichever persona you'd want) has access to the resource, via
PermissionChecker.

Example:
Expand All @@ -72,18 +78,25 @@ Example:
@Stateless
class FooService {
@Inject
HawkularUser user;
Persona persona;
@Inject
PermissionChecker permissionChecker;
@Inject
ResourceService resourceService;
@Inject
@NamedOperation("foo-read")
Operation operationRead;
public void createFoo() {
Foo foo = new Foo(id); // this is your business resource
// the ID can be anything that is unique. When omitted, a new UUID is created and you'll be responsible for
// storing it somewhere and informing it next time you need access to this resource. Also, we need to
// explicitly know who is the owner.
Resource resource = new Resource(id, user);
resourceService.create(foo.getId(), persona);
// your storage might be Cassandra, JPA-based storage, in memory map, ...
storage.persist(foo);
Expand All @@ -93,7 +106,7 @@ class FooService {
Foo foo = storage.getFoo(id); // this is your business resource
// when omitted, the user is assumed to be the current user on the permission checker
if (permissionChecker.hasAccessTo(id)) {
if (permissionChecker.isAllowedTo(operationRead, foo.getId())) {
// return it to the user
} else {
// return a forbidden message
Expand All @@ -102,11 +115,44 @@ class FooService {
}
----



Note that Hawkular Accounts depends on Keycloak to work, so, be sure to either run it on Kettle or configure a Keycloak
instance appropriately.

== Operations and roles

As a one-time setup, the permission set needs to be configured. With that, a list of operations need to be provided,
as well as which roles are allowed to perform those operations. To facilitate this procedure, the OrganizationService
can be used as a @Singleton @Startup EJB, with the setup on a @PostConstruct method. For example:

[source,java]
----
operationService
.setup("organization-create")
.add("Monitor") // means: all roles
.commit()
.setup("organization-read")
.add("Maintainer")
.commit()
.setup("organization-delete")
.add("Super User")
.commit()
.setup("organization-update")
.add("Maintainer")
.commit();
----

Hawkular Accounts ships with the same roles as Wildfly and with the same rules (ie: Super User will be given permission
to perform operations marked as allowed for "user with at least Monitor role"). So, adding the role "Monitor" during
the setup will automatically add all other roles to it.

Note as well that if the set of roles for a given operation has not changed from what we currently have on the
database, nothing is performed.

More about the Wildfly roles can be found on this
link:http://blog.arungupta.me/role-based-access-control-wildfly-8/[blog post]

== Setup

Expand Down
23 changes: 23 additions & 0 deletions accounts/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<scope>provided</scope>
</dependency>

Expand All @@ -100,5 +109,19 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Dependencies>org.infinispan export</Dependencies>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
package org.hawkular.accounts.backend.boundary;

import org.hawkular.accounts.api.PermissionChecker;
import org.hawkular.accounts.api.ResourceService;
import org.hawkular.accounts.api.internal.adapter.HawkularAccounts;
import org.hawkular.accounts.api.internal.adapter.NamedOperation;
import org.hawkular.accounts.api.model.HawkularUser;
import org.hawkular.accounts.api.model.Operation;
import org.hawkular.accounts.api.model.Organization;
import org.hawkular.accounts.api.model.Organization_;
import org.hawkular.accounts.backend.entity.rest.OrganizationRequest;
Expand All @@ -41,12 +44,12 @@
/**
* REST service responsible for managing {@link org.hawkular.accounts.api.model.Organization}.
*
* @author jpkroehling
* @author Juraci Paixão Kröhling
*/
@Path("/organizations")
@PermitAll
@Stateless
public class OrganizationService {
public class OrganizationEndpoint {
@Inject @HawkularAccounts
EntityManager em;

Expand All @@ -56,6 +59,25 @@ public class OrganizationService {
@Inject
PermissionChecker permissionChecker;

@Inject
@NamedOperation("organization-create")
Operation operationCreate;

@Inject
@NamedOperation("organization-read")
Operation operationRead;

@Inject
@NamedOperation("organization-update")
Operation operationUpdate;

@Inject
@NamedOperation("organization-delete")
Operation operationDelete;

@Inject
ResourceService resourceService;

/**
* Retrieves all organizations to which this {@link org.hawkular.accounts.api.model.HawkularUser} has access to.
*
Expand Down Expand Up @@ -86,6 +108,7 @@ public Response getOrganizations() {
@Path("/")
public Response createOrganization(@NotNull OrganizationRequest request) {
Organization organization = new Organization(user);
resourceService.create(organization.getId(), user);

organization.setName(request.getName());
organization.setDescription(request.getDescription());
Expand All @@ -105,8 +128,7 @@ public Response createOrganization(@NotNull OrganizationRequest request) {
@Path("/{id}")
public Response deleteOrganization(@NotNull @PathParam("id") String id) {
Organization organization = em.find(Organization.class, id);

if (permissionChecker.isOwnerOf(user, organization)) {
if (permissionChecker.isAllowedTo(operationDelete, id, user)) {
em.remove(organization);
return Response.ok().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
* Contains the components that interacts with external interfaces, such as REST endpoints. All requests are expected
* to have been already authenticated by a service via the container.
*
* @author jpkroehling
* @author Juraci Paixão Kröhling
*/
package org.hawkular.accounts.backend.boundary;
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/**
* Integration point with JAX-RS. Specifies that we have a JAX-RS application, on the namespace "/".
*
* @author Juraci Paixão Kröhling <juraci at kroehling.de>
* @author Juraci Paixão Kröhling
*/
@ApplicationPath("/")
public class JaxRsActivator extends Application {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hawkular.accounts.backend.control;

import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.ValidIdRange;

/**
* JBoss Logging integration, with the possible messages that we have for this backend.
*
* @author Juraci Paixão Kröhling
*/
@org.jboss.logging.annotations.MessageLogger(projectCode = "HAWKACC")
@ValidIdRange(min = 110000, max = 119999)
public interface MsgLogger {
MsgLogger LOGGER = Logger.getMessageLogger(MsgLogger.class, MsgLogger.class.getPackage().getName());

@LogMessage(level = Logger.Level.INFO)
@Message(id = 110000, value = "Started setting up Hawkular Accounts")
void infoStartedSetupAccounts();

@LogMessage(level = Logger.Level.INFO)
@Message(id = 110001, value = "Finished setting up Hawkular Accounts")
void infoFinishedSetupAccounts();
}

0 comments on commit 438e877

Please sign in to comment.