/** * * Copyright * 2009-2015 Jayway Products AB * 2016-2017 Föreningen Sambruk * * Licensed under AGPL, Version 3.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.gnu.org/licenses/agpl.txt * * 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 se.streamsource.streamflow.web.domain.structure.organization; import static org.qi4j.api.entity.EntityReference.getEntityReference; import java.security.Principal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.qi4j.api.common.Optional; import org.qi4j.api.common.UseDefaults; import org.qi4j.api.concern.ConcernOf; import org.qi4j.api.concern.Concerns; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.injection.scope.This; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.property.Property; import org.qi4j.api.structure.Module; import org.qi4j.api.unitofwork.NoSuchEntityException; import org.qi4j.api.unitofwork.UnitOfWork; import org.qi4j.api.value.ValueBuilder; import se.streamsource.dci.api.RoleMap; import se.streamsource.streamflow.infrastructure.event.domain.DomainEvent; import se.streamsource.streamflow.web.domain.structure.group.Participant; import se.streamsource.streamflow.web.domain.structure.group.Participants; import se.streamsource.streamflow.web.domain.structure.role.Role; /** * Policy for managing Roles assigned to Participants. Participants * can have a list of Roles assigned to them, which can be granted and revoked. */ @Concerns( RolePolicy.GrantRoleConcern.class) @Mixins(RolePolicy.Mixin.class) public interface RolePolicy { void grantRole( Participant participant, Role role ); void revokeRole( Participant participant, Role role ); void revokeRoles( Participant participant ); void grantAdministratorToCurrentUser(); void mergeRolePolicy(RolePolicy to); boolean participantHasRole( Participant participant, Role role ); boolean participantHasPermission( String participant, String permission ); List<Participant> participantsWithRole( Role role ); boolean hasRoles( Participant participant ); interface Data { @UseDefaults Property<List<ParticipantRolesValue>> policy(); void grantedRole( @Optional DomainEvent event, Participant participant, Role role ); void revokedRole( @Optional DomainEvent event, Participant participant, Role role ); } abstract class Mixin implements RolePolicy, Data { @Structure Module module; @This OwningOrganization orgOwner; public void grantRole( Participant participant, Role role ) { if (participantHasRole( participant, role )) return; grantedRole( null, participant, role ); } public void revokeRole( Participant participant, Role role ) { if (!participantHasRole( participant, role )) return; revokedRole( null, participant, role ); } public void revokeRoles( Participant participant ) { if (hasRoles( participant )) { ParticipantRolesValue roles = getRoles( participant ); for (EntityReference entityReference : roles.roles().get()) { Role role = module.unitOfWorkFactory().currentUnitOfWork().get( Role.class, entityReference.identity() ); revokeRole( participant, role ); } } } public void grantAdministratorToCurrentUser() { Principal principal = RoleMap.role( Principal.class ); Participant user = module.unitOfWorkFactory().currentUnitOfWork().get( Participant.class, principal.getName() ); Organization org = orgOwner.organization().get(); Role administrator = org.getAdministratorRole(); grantRole( user, administrator ); } public void mergeRolePolicy( RolePolicy to ) { for (ParticipantRolesValue participantRolesValue : policy().get()) { Participant participant = module.unitOfWorkFactory().currentUnitOfWork().get( Participant.class, participantRolesValue.participant().get().identity() ); for (EntityReference entityReference : participantRolesValue.roles().get()) { Role role = module.unitOfWorkFactory().currentUnitOfWork().get( Role.class, entityReference.identity() ); to.grantRole( participant, role); } } } public void grantedRole( @Optional DomainEvent event, Participant participant, Role role ) { EntityReference participantRef = getEntityReference( participant ); List<ParticipantRolesValue> participantRoles = policy().get(); int idx = 0; for (ParticipantRolesValue participantRole : participantRoles) { if (participantRole.participant().get().equals( participantRef )) { // Add role to list EntityReference roleRef = getEntityReference( role ); ValueBuilder<ParticipantRolesValue> builder = participantRole.buildWith(); builder.prototype().roles().get().add( roleRef ); participantRoles.set( idx, builder.newInstance() ); policy().set( participantRoles ); return; } idx++; } // Participant is not in list - add it EntityReference roleRef = getEntityReference( role ); ValueBuilder<ParticipantRolesValue> builder = module.valueBuilderFactory().newValueBuilder(ParticipantRolesValue.class); builder.prototype().participant().set( participantRef ); builder.prototype().roles().get().add( roleRef ); List<ParticipantRolesValue> policy = policy().get(); policy.add( builder.newInstance() ); policy().set( policy ); } public void revokedRole( @Optional DomainEvent event, Participant participant, Role role ) { EntityReference participantRef = getEntityReference( participant ); List<ParticipantRolesValue> participantRoles = policy().get(); int idx = 0; for (ParticipantRolesValue participantRole : participantRoles) { if (participantRole.participant().get().equals( participantRef )) { // Remove role from list EntityReference roleRef = getEntityReference( role ); ValueBuilder<ParticipantRolesValue> builder = participantRole.buildWith(); builder.prototype().roles().get().remove( roleRef ); participantRoles.set( idx, builder.newInstance() ); policy().set( participantRoles ); return; } idx++; } } public boolean participantHasRole( Participant participant, Role role ) { // Check if user already has role ParticipantRolesValue participantRolesValue = getRoles( participant ); if (participantRolesValue != null) { EntityReference roleRef = getEntityReference( role ); for (EntityReference participantRole : participantRolesValue.roles().get()) { if (participantRole.equals( roleRef )) return true; } } return false; } public boolean participantHasPermission( String participantId, String permission ) { UnitOfWork uow = module.unitOfWorkFactory().currentUnitOfWork(); Participant participant = uow.get( Participant.class, participantId ); // Check if user already has role ParticipantRolesValue participantRolesValue = getRoles( participant ); if (participantRolesValue != null) { for (EntityReference participantRole : participantRolesValue.roles().get()) { Role role = uow.get( Role.class, participantRole.identity() ); if (role.hasPermission( permission )) return true; } } return false; } public ParticipantRolesValue getRoles( Participant participant ) { Set<EntityReference> mergedRoles = new HashSet<EntityReference>( ); UnitOfWork uow = module.unitOfWorkFactory().currentUnitOfWork(); EntityReference participantRef = getEntityReference( participant ); for (ParticipantRolesValue participantRolesValue : policy().get()) { Participant possibleGroup = null; try { possibleGroup = uow.get( Participant.class, participantRolesValue.participant().get().identity() ); } catch(NoSuchEntityException ne ) { // ok - do nothing } if ( !participantRolesValue.participant().get().equals( participantRef ) && possibleGroup instanceof Participants) { if( ((Participants)possibleGroup).isParticipant( participant ) ) { mergedRoles.addAll( participantRolesValue.roles().get() ); } } else { if ( participantRolesValue.participant().get().equals( participantRef )) { mergedRoles.addAll( participantRolesValue.roles().get() ); } } } // compile a merged list of roles and set it on a new ParticipantRolesValue to return ValueBuilder<ParticipantRolesValue> builder = module.valueBuilderFactory().newValueBuilder( ParticipantRolesValue.class ); builder.prototype().participant().set( participantRef ); builder.prototype().roles().set( new ArrayList<EntityReference>( mergedRoles ) ); return builder.newInstance(); } public List<Participant> participantsWithRole( Role role ) { UnitOfWork uow = module.unitOfWorkFactory().currentUnitOfWork(); List<Participant> participants = new ArrayList<Participant>(); EntityReference roleRef = getEntityReference( role ); for (ParticipantRolesValue participantRolesValue : policy().get()) { for (EntityReference participantRole : participantRolesValue.roles().get()) { if (participantRole.equals( roleRef )) { participants.add( uow.get( Participant.class, participantRolesValue.participant().get().identity() ) ); break; } } } return participants; } public boolean hasRoles( Participant participant ) { ParticipantRolesValue value = getRoles( participant ); return value != null && !value.roles().get().isEmpty(); } } abstract class GrantRoleConcern extends ConcernOf<RolePolicy> implements RolePolicy { @This OrganizationalUnits.Data orgUnits; public void grantRole( Participant participant, Role role ) { next.grantRole( participant, role ); for( OrganizationalUnits ou : orgUnits.organizationalUnits() ) { ((RolePolicy)ou).grantRole( participant, role ); } } public void revokeRole( Participant participant, Role role ) { next.revokeRole( participant, role ); for( OrganizationalUnits ou : orgUnits.organizationalUnits() ) { ((RolePolicy)ou).revokeRole( participant, role ); } } } }