You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/02/12 20:18:03 UTC

[isis] branch master updated: ISIS-2158: SecMan: starting to extract mixins for all actions

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 62113e4  ISIS-2158: SecMan: starting to extract mixins for all actions
62113e4 is described below

commit 62113e490387d007b6c017bba6bad4c2b62a0cc8
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Feb 12 21:17:50 2020 +0100

    ISIS-2158: SecMan: starting to extract mixins for all actions
    
    also moving these mixins from 'jdo' to 'model'
---
 .../api/permission/ApplicationPermission.java      |  27 +++-
 .../secman/api/role/ApplicationRole.java           |  11 ++
 .../secman/api/role/ApplicationRoleRepository.java |  16 ++-
 .../secman/api/tenancy/ApplicationTenancy.java     |  17 +--
 .../api/tenancy/ApplicationTenancyRepository.java  |  15 ++-
 .../secman/api/user/ApplicationUser.java           |  16 +++
 .../secman/api/user/ApplicationUserRepository.java |   8 +-
 .../permission/ApplicationPermission_allow.java    |  44 ++++++
 .../permission/ApplicationPermission_changing.java |  45 +++++++
 .../permission/ApplicationPermission_delete.java}  |  48 +++----
 .../ApplicationPermission_updateRole.java          |  55 ++++++++
 .../dom/permission/ApplicationPermission_veto.java |  44 ++++++
 .../permission/ApplicationPermission_viewing.java  |  45 +++++++
 .../secman/model/dom/role/ApplicationRoleMenu.java |   6 +-
 .../model/dom/role/ApplicationRole_addUser.java    |  60 +++++++++
 .../model/dom/role/ApplicationRole_removeUser.java |  63 +++++++++
 .../model/dom/tenancy/ApplicationTenancyMenu.java  |   6 +-
 .../dom/tenancy/ApplicationTenancy_addUser.java    |  60 +++++++++
 .../dom/tenancy/ApplicationTenancy_removeUser.java |  56 ++++++++
 .../secman/model/dom/user/ApplicationUserMenu.java |   6 +-
 .../model/dom/user/ApplicationUser_addRole.java    |  62 +++++++++
 .../jdo/dom/permission/ApplicationPermission.java  | 149 +++------------------
 .../secman/jdo/dom/role/ApplicationRole.java       | 102 ++------------
 .../jdo/dom/role/ApplicationRoleRepository.java    |  81 +++++++++--
 .../secman/jdo/dom/tenancy/ApplicationTenancy.java |  56 +-------
 .../dom/tenancy/ApplicationTenancyRepository.java  |  86 ++++++++----
 .../secman/jdo/dom/user/ApplicationUser.java       |  62 ++-------
 .../jdo/dom/user/ApplicationUserRepository.java    |  35 +++--
 .../scripts/AbstractUserAndRolesFixtureScript.java |   7 +-
 ...tyModuleAppUserRegistrationServiceAbstract.java |  10 +-
 30 files changed, 855 insertions(+), 443 deletions(-)

diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/permission/ApplicationPermission.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/permission/ApplicationPermission.java
index 4f980a5..21bd5c2 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/permission/ApplicationPermission.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/permission/ApplicationPermission.java
@@ -19,11 +19,36 @@
 package org.apache.isis.extensions.secman.api.permission;
 
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureType;
+import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole;
 
 public interface ApplicationPermission {
-
+    
     ApplicationFeatureType getFeatureType();
 
     String getFeatureFqn();
+    
+    ApplicationPermissionRule getRule();
+    void setRule(ApplicationPermissionRule rule);
+    
+    ApplicationPermissionMode getMode();
+    void setMode(ApplicationPermissionMode changing);
+    
+    ApplicationRole getRole();
+    void setRole(ApplicationRole applicationRole);
+    
+    // DOMAIN EVENTS
+    
+    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationPermission, T> {}
+    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationPermission, T> {}
+    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationPermission> {}
+    
+    public static class AllowDomainEvent extends ActionDomainEvent {}
+    public static class UpdateRoleDomainEvent extends ActionDomainEvent {}
+    public static class VetoDomainEvent extends ActionDomainEvent {}
+    public static class DeleteDomainEvent extends ActionDomainEvent {}
+    public static class ChangingDomainEvent extends ActionDomainEvent {}
+    public static class ViewingDomainEvent extends ActionDomainEvent {}
+    
 
 }
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRole.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRole.java
index 4e15862..d3a4741 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRole.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRole.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.extensions.secman.api.role;
 
+import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
+
 public interface ApplicationRole {
 
     public static final int MAX_LENGTH_NAME = 50;
@@ -25,5 +27,14 @@ public interface ApplicationRole {
     public static final int TYPICAL_LENGTH_DESCRIPTION = 50;
 
     String getName();
+    
+    // -- EVENTS
+    
+    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationRole, T> {}
+    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationRole, T> {}
+    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationRole> {}
+    
+    public static class AddUserDomainEvent extends ActionDomainEvent {}
+    public static class RemoveUserDomainEvent extends ActionDomainEvent {}
 
 }
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRoleRepository.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRoleRepository.java
index c965563..bec38d6 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRoleRepository.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/role/ApplicationRoleRepository.java
@@ -18,18 +18,28 @@
  */
 package org.apache.isis.extensions.secman.api.role;
 
-import java.util.List;
+import java.util.Collection;
+
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
 
 public interface ApplicationRoleRepository {
 
-    List<? extends ApplicationRole> allRoles();
+    Collection<ApplicationRole> allRoles();
 
     ApplicationRole newRole(String name, String description);
 
-    List<? extends ApplicationRole> findNameContaining(String search);
+    Collection<ApplicationRole> findNameContaining(String search);
+    
+    Collection<ApplicationUser> getUsers(ApplicationRole role);
 
     ApplicationRole findByName(String roleName);
 
     ApplicationRole findByNameCached(String roleName);
 
+    void addRoleToUser(ApplicationRole role, ApplicationUser holder);
+    void removeRoleFromUser(ApplicationRole role, ApplicationUser holder);
+
+    boolean isAdminRole(ApplicationRole role);
+    
+
 }
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancy.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancy.java
index 45feb81..ecca736 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancy.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancy.java
@@ -22,16 +22,17 @@ import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
 
 public interface ApplicationTenancy {
 
-    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationTenancy, T> {}
-
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationTenancy, T> {}
-
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationTenancy> {}
-
-
-
     public static final int MAX_LENGTH_PATH = 255;
     public static final int MAX_LENGTH_NAME = 40;
     public static final int TYPICAL_LENGTH_NAME = 20;
+    
+    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationTenancy, T> {}
+    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationTenancy, T> {}
+    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationTenancy> {}
+    
+    public static class AddUserDomainEvent extends ActionDomainEvent {}
+    public static class RemoveUserDomainEvent extends ActionDomainEvent {}
+    
+    public Object getPath();
 
 }
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancyRepository.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancyRepository.java
index d06663b..6557f2b 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancyRepository.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/tenancy/ApplicationTenancyRepository.java
@@ -18,14 +18,23 @@
  */
 package org.apache.isis.extensions.secman.api.tenancy;
 
-import java.util.List;
+import java.util.Collection;
+
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
 
 public interface ApplicationTenancyRepository {
 
-    List<? extends ApplicationTenancy> allTenancies();
+    Collection<ApplicationTenancy> allTenancies();
 
-    List<? extends ApplicationTenancy> findByNameOrPathMatchingCached(String partialNameOrPath);
+    Collection<ApplicationTenancy> findByNameOrPathMatchingCached(String partialNameOrPath);
+    
+    Collection<ApplicationUser> getUsers(ApplicationTenancy tenancy);
 
     ApplicationTenancy newTenancy(String name, String path, ApplicationTenancy parent);
 
+    void setTenancyOnUser(ApplicationTenancy tenancy, ApplicationUser user);
+    void clearTenancyOnUser(ApplicationUser user);
+
+    
+
 }
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java
index 0a00127..45581de 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java
@@ -21,6 +21,7 @@ package org.apache.isis.extensions.secman.api.user;
 import java.util.SortedSet;
 
 import org.apache.isis.applib.services.HasUsername;
+import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionValueSet;
 import org.apache.isis.extensions.secman.api.role.ApplicationRole;
 import org.apache.isis.extensions.secman.api.tenancy.HasAtPath;
@@ -51,5 +52,20 @@ public interface ApplicationUser extends HasUsername, HasAtPath {
 
     @Override
     String getUsername();
+    
+    // -- EVENTS 
+    
+    public static abstract class PropertyDomainEvent<T>
+    extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationUser, T> {}
+
+    public static abstract class CollectionDomainEvent<T> 
+    extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationUser, T> {}
+
+    public static abstract class ActionDomainEvent 
+    extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationUser> {}
+    
+    public static class AddRoleDomainEvent extends ActionDomainEvent {}
+
+    
 
 }
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUserRepository.java b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUserRepository.java
index 0cab3b9..3698527 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUserRepository.java
+++ b/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUserRepository.java
@@ -18,7 +18,7 @@
  */
 package org.apache.isis.extensions.secman.api.user;
 
-import java.util.List;
+import java.util.Collection;
 
 import org.apache.isis.applib.value.Password;
 import org.apache.isis.extensions.secman.api.role.ApplicationRole;
@@ -29,9 +29,9 @@ public interface ApplicationUserRepository {
 
     ApplicationUser findOrCreateUserByUsername(String username);
 
-    List<? extends ApplicationUser> allUsers();
+    Collection<ApplicationUser> allUsers();
 
-    List<? extends ApplicationUser> find(String search);
+    Collection<ApplicationUser> find(String search);
 
     String validateNewLocalUser(String username, Password password, Password passwordRepeat,
             ApplicationRole initialRole, Boolean enabled, String emailAddress);
@@ -44,4 +44,6 @@ public interface ApplicationUserRepository {
     void enable(ApplicationUser svenUser);
     void disable(ApplicationUser svenUser);
 
+    boolean isAdminUser(ApplicationUser user);
+
 }
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_allow.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_allow.java
new file mode 100644
index 0000000..05f7213
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_allow.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.permission;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission.AllowDomainEvent;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = AllowDomainEvent.class, associateWith = "rule")
+@RequiredArgsConstructor
+public class ApplicationPermission_allow {
+
+    private final ApplicationPermission holder;
+
+    //@MemberOrder(name = "Rule", sequence = "1")
+    public ApplicationPermission act() {
+        holder.setRule(ApplicationPermissionRule.ALLOW);
+        return holder;
+    }
+
+    public String disableAct() {
+        return holder.getRule() == ApplicationPermissionRule.ALLOW? "Rule is already set to ALLOW": null;
+    }
+
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_changing.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_changing.java
new file mode 100644
index 0000000..f4c2f3c
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_changing.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.permission;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission.ChangingDomainEvent;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = ChangingDomainEvent.class, associateWith = "mode")
+@RequiredArgsConstructor
+public class ApplicationPermission_changing {
+
+    private final ApplicationPermission holder;
+
+    //@MemberOrder(name = "Mode", sequence = "2")
+    public ApplicationPermission act() {
+        holder.setMode(ApplicationPermissionMode.CHANGING);
+        return holder;
+    }
+    
+    public String disableAct() {
+        return holder.getMode() == ApplicationPermissionMode.CHANGING ? "Mode is already set to CHANGING": null;
+    }
+    
+
+}
diff --git a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_delete.java
similarity index 51%
copy from extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java
copy to extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_delete.java
index 0a00127..0f7194c 100644
--- a/extensions/security/secman/api/src/main/java/org/apache/isis/extensions/secman/api/user/ApplicationUser.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_delete.java
@@ -16,40 +16,32 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.secman.api.user;
+package org.apache.isis.extensions.secman.model.dom.permission;
 
-import java.util.SortedSet;
+import javax.inject.Inject;
 
-import org.apache.isis.applib.services.HasUsername;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionValueSet;
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission.DeleteDomainEvent;
 import org.apache.isis.extensions.secman.api.role.ApplicationRole;
-import org.apache.isis.extensions.secman.api.tenancy.HasAtPath;
 
+import lombok.RequiredArgsConstructor;
+import lombok.val;
 
-public interface ApplicationUser extends HasUsername, HasAtPath {
+@Action(domainEvent = DeleteDomainEvent.class, semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE)
+@RequiredArgsConstructor
+public class ApplicationPermission_delete {
+    
+    @Inject private RepositoryService repository;
 
-    // -- constants
-    public static final int MAX_LENGTH_USERNAME = 30;
-    public static final int MAX_LENGTH_FAMILY_NAME = 50;
-    public static final int MAX_LENGTH_GIVEN_NAME = 50;
-    public static final int MAX_LENGTH_KNOWN_AS = 20;
-    public static final int MAX_LENGTH_EMAIL_ADDRESS = 50;
-    public static final int MAX_LENGTH_PHONE_NUMBER = 25;
+    private final ApplicationPermission holder;
 
-
-    String getName();
-
-    String getEncryptedPassword();
-
-    AccountType getAccountType();
-
-    ApplicationPermissionValueSet getPermissionSet();
-
-    SortedSet<? extends ApplicationRole> getRoles();
-
-    ApplicationUserStatus getStatus();
-
-    @Override
-    String getUsername();
+    public ApplicationRole act() {
+        val owningRole = holder.getRole();
+        repository.remove(holder);
+        return owningRole;
+    }
 
 }
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_updateRole.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_updateRole.java
new file mode 100644
index 0000000..35c2af7
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_updateRole.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.permission;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission.UpdateRoleDomainEvent;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole;
+import org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = UpdateRoleDomainEvent.class, associateWith = "role")
+@RequiredArgsConstructor
+public class ApplicationPermission_updateRole {
+
+    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    
+    private final ApplicationPermission holder;
+    
+    //@MemberOrder(name="Role", sequence = "1")
+    public ApplicationPermission act(final ApplicationRole applicationRole) {
+        holder.setRole(applicationRole);
+        return holder;
+    }
+
+    public ApplicationRole default0Act() {
+        return holder.getRole();
+    }
+
+    public Collection<? extends ApplicationRole> choices0Act() {
+        return applicationRoleRepository.allRoles();
+    }
+    
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_veto.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_veto.java
new file mode 100644
index 0000000..0d5ee2e
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_veto.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.permission;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission.VetoDomainEvent;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = VetoDomainEvent.class, associateWith = "rule")
+@RequiredArgsConstructor
+public class ApplicationPermission_veto {
+
+    private final ApplicationPermission holder;
+
+    //@MemberOrder(name = "Rule", sequence = "1")
+    public ApplicationPermission act() {
+        holder.setRule(ApplicationPermissionRule.VETO);
+        return holder;
+    }
+    public String disableAct() {
+        return holder.getRule() == ApplicationPermissionRule.VETO? "Rule is already set to VETO": null;
+    }
+    
+
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_viewing.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_viewing.java
new file mode 100644
index 0000000..98e96e3
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationPermission_viewing.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.permission;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermission.ViewingDomainEvent;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = ViewingDomainEvent.class, associateWith = "mode")
+@RequiredArgsConstructor
+public class ApplicationPermission_viewing {
+
+    private final ApplicationPermission holder;
+    
+    //@MemberOrder(name = "Mode", sequence = "1")
+    public ApplicationPermission act() {
+        holder.setMode(ApplicationPermissionMode.VIEWING);
+        return holder;
+    }
+    
+    public String disableAct() {
+        return holder.getMode() == ApplicationPermissionMode.VIEWING ? "Mode is already set to VIEWING": null;
+    }
+    
+
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRoleMenu.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRoleMenu.java
index 606c277..917404e 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRoleMenu.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRoleMenu.java
@@ -18,7 +18,7 @@
  */
 package org.apache.isis.extensions.secman.model.dom.role;
 
-import java.util.List;
+import java.util.Collection;
 
 import javax.inject.Inject;
 
@@ -68,7 +68,7 @@ public class  ApplicationRoleMenu {
             semantics = SemanticsOf.SAFE
             )
     @MemberOrder(sequence = "100.20.1")
-    public List<? extends ApplicationRole> findRoles(
+    public Collection<ApplicationRole> findRoles(
             @Parameter(maxLength = ApplicationRole.MAX_LENGTH_NAME)
             @ParameterLayout(named = "Search", typicalLength = ApplicationRole.TYPICAL_LENGTH_NAME)
             final String search) {
@@ -103,7 +103,7 @@ public class  ApplicationRoleMenu {
             semantics = SemanticsOf.SAFE
             )
     @MemberOrder(sequence = "100.20.3")
-    public List<? extends ApplicationRole> allRoles() {
+    public Collection<ApplicationRole> allRoles() {
         return applicationRoleRepository.allRoles();
     }
 
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addUser.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addUser.java
new file mode 100644
index 0000000..49296a4
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addUser.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.role;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole.AddUserDomainEvent;
+import org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository;
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
+import org.apache.isis.extensions.secman.api.user.ApplicationUserRepository;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = AddUserDomainEvent.class, associateWith = "users")
+@ActionLayout(named="Add")
+@RequiredArgsConstructor
+public class ApplicationRole_addUser {
+    
+    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    @Inject private ApplicationUserRepository applicationUserRepository;
+    
+    private final ApplicationRole holder;
+
+    @MemberOrder(sequence = "1")
+    public ApplicationRole act(final ApplicationUser applicationUser) {
+        applicationRoleRepository.addRoleToUser(holder, applicationUser);
+        return holder;
+    }
+
+    public List<ApplicationUser> autoComplete0Act(final String search) {
+        final Collection<ApplicationUser> matchingSearch = applicationUserRepository.find(search);
+        final List<ApplicationUser> list = _Lists.newArrayList(matchingSearch);
+        list.removeAll(applicationRoleRepository.getUsers(holder));
+        return list;
+    }
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_removeUser.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_removeUser.java
new file mode 100644
index 0000000..974eb30
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_removeUser.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.role;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole.RemoveUserDomainEvent;
+import org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository;
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
+import org.apache.isis.extensions.secman.api.user.ApplicationUserRepository;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = RemoveUserDomainEvent.class, associateWith = "users")
+@ActionLayout(named="Remove")
+@RequiredArgsConstructor
+public class ApplicationRole_removeUser {
+    
+    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    @Inject private ApplicationUserRepository applicationUserRepository;
+    
+    private final ApplicationRole holder;
+
+    @MemberOrder(sequence = "2")
+    public ApplicationRole act(final ApplicationUser applicationUser) {
+        applicationRoleRepository.removeRoleFromUser(holder, applicationUser);
+        return holder;
+    }
+
+    public Collection<ApplicationUser> choices0Act() {
+        return applicationRoleRepository.getUsers(holder);
+    }
+
+    public String validateAct(final ApplicationUser applicationUser) {
+        if(applicationUserRepository.isAdminUser(applicationUser) 
+                && applicationRoleRepository.isAdminRole(holder)) {
+            return "Cannot remove admin user from the admin role.";
+        }
+        return null;
+    }
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancyMenu.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancyMenu.java
index 7c7f3bf..8469126 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancyMenu.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancyMenu.java
@@ -18,7 +18,7 @@
  */
 package org.apache.isis.extensions.secman.model.dom.tenancy;
 
-import java.util.List;
+import java.util.Collection;
 
 import javax.inject.Inject;
 
@@ -69,7 +69,7 @@ public class ApplicationTenancyMenu {
             semantics = SemanticsOf.SAFE
             )
     @MemberOrder(sequence = "100.30.1")
-    public List<? extends ApplicationTenancy> findTenancies(
+    public Collection<ApplicationTenancy> findTenancies(
             @Parameter(optionality = Optionality.OPTIONAL)
             @ParameterLayout(named = "Partial Name Or Path", describedAs = "String to search for, wildcard (*) can be used")
             @MinLength(1) // for auto-complete
@@ -109,7 +109,7 @@ public class ApplicationTenancyMenu {
             restrictTo = RestrictTo.PROTOTYPING
             )
     @MemberOrder(sequence = "100.30.4")
-    public List<? extends ApplicationTenancy> allTenancies() {
+    public Collection<ApplicationTenancy> allTenancies() {
         return applicationTenancyRepository.allTenancies();
     }
 
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancy_addUser.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancy_addUser.java
new file mode 100644
index 0000000..dc943b9
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancy_addUser.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.tenancy;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy;
+import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy.AddUserDomainEvent;
+import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancyRepository;
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
+import org.apache.isis.extensions.secman.api.user.ApplicationUserRepository;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = AddUserDomainEvent.class, associateWith = "users")
+@ActionLayout(named="Add")
+@RequiredArgsConstructor
+public class ApplicationTenancy_addUser {
+    
+    @Inject private ApplicationTenancyRepository applicationTenancyRepository;
+    @Inject private ApplicationUserRepository applicationUserRepository;
+    
+    private final ApplicationTenancy holder;
+
+    @MemberOrder(sequence = "1")
+    public ApplicationTenancy act(final ApplicationUser applicationUser) {
+        applicationTenancyRepository.setTenancyOnUser(holder, applicationUser);
+        return holder;
+    }
+
+    public List<ApplicationUser> autoComplete0Act(final String search) {
+        final Collection<ApplicationUser> matchingSearch = applicationUserRepository.find(search);
+        final List<ApplicationUser> list = _Lists.newArrayList(matchingSearch);
+        list.removeAll(applicationTenancyRepository.getUsers(holder));
+        return list;
+    }
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancy_removeUser.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancy_removeUser.java
new file mode 100644
index 0000000..6000886
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/tenancy/ApplicationTenancy_removeUser.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.tenancy;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy;
+import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy.RemoveUserDomainEvent;
+import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancyRepository;
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = RemoveUserDomainEvent.class, associateWith = "users")
+@ActionLayout(named="Remove")
+@RequiredArgsConstructor
+public class ApplicationTenancy_removeUser {
+    
+    @Inject private ApplicationTenancyRepository applicationTenancyRepository;
+    
+    private final ApplicationTenancy holder;
+    
+    @MemberOrder(sequence = "2")
+    public ApplicationTenancy act(final ApplicationUser applicationUser) {
+        applicationTenancyRepository.clearTenancyOnUser(applicationUser);
+        return holder;
+    }
+    
+    public java.util.Collection<ApplicationUser> choices0Act() {
+        return applicationTenancyRepository.getUsers(holder);
+    }
+    
+    public String disableAct() {
+        return choices0Act().isEmpty()? "No users to remove": null;
+    }
+
+}
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUserMenu.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUserMenu.java
index 3d22f89..97aa9f3 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUserMenu.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUserMenu.java
@@ -18,7 +18,7 @@
  */
 package org.apache.isis.extensions.secman.model.dom.user;
 
-import java.util.List;
+import java.util.Collection;
 
 import javax.inject.Inject;
 
@@ -79,7 +79,7 @@ public class ApplicationUserMenu {
             semantics = SemanticsOf.SAFE
             )
     @MemberOrder(sequence = "100.10.2")
-    public List<? extends ApplicationUser> findUsers(
+    public Collection<ApplicationUser> findUsers(
             final @ParameterLayout(named = "Search") String search) {
         return applicationUserRepository.find(search);
     }
@@ -167,7 +167,7 @@ public class ApplicationUserMenu {
             semantics = SemanticsOf.SAFE
             )
     @MemberOrder(sequence = "100.10.5")
-    public List<? extends ApplicationUser> allUsers() {
+    public Collection<ApplicationUser> allUsers() {
         return applicationUserRepository.allUsers();
     }
 
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_addRole.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_addRole.java
new file mode 100644
index 0000000..1d21131
--- /dev/null
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_addRole.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.isis.extensions.secman.model.dom.user;
+
+import java.util.SortedSet;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.core.commons.internal.collections._Sets;
+import org.apache.isis.extensions.secman.api.role.ApplicationRole;
+import org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository;
+import org.apache.isis.extensions.secman.api.user.ApplicationUser;
+import org.apache.isis.extensions.secman.api.user.ApplicationUser.AddRoleDomainEvent;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(domainEvent = AddRoleDomainEvent.class, associateWith = "roles")
+@ActionLayout(named="Add")
+@RequiredArgsConstructor
+public class ApplicationUser_addRole {
+    
+    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    
+    private final ApplicationUser holder;
+
+    @MemberOrder(sequence = "1")
+    public ApplicationUser act(final ApplicationRole role) {
+        applicationRoleRepository.addRoleToUser(role, holder);
+        return holder;
+    }
+
+    public SortedSet<ApplicationRole> choices0Act() {
+        final java.util.Collection<ApplicationRole> allRoles = applicationRoleRepository.allRoles();
+        final SortedSet<ApplicationRole> applicationRoles = _Sets.newTreeSet(allRoles);
+        applicationRoles.removeAll(holder.getRoles());
+        return applicationRoles;
+    }
+
+    public String disableAct() {
+        return choices0Act().isEmpty()? "All roles added": null;
+    }
+
+}
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/permission/ApplicationPermission.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/permission/ApplicationPermission.java
index 5cc15e7..a4441d7 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/permission/ApplicationPermission.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/permission/ApplicationPermission.java
@@ -28,7 +28,6 @@ import javax.jdo.annotations.IdentityType;
 import javax.jdo.annotations.InheritanceStrategy;
 import javax.jdo.annotations.VersionStrategy;
 
-import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.BookmarkPolicy;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
@@ -37,21 +36,20 @@ import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
-import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.appfeat.ApplicationMemberType;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ObjectContracts.ObjectContract;
-import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionValue;
-import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRole;
+import org.apache.isis.core.commons.internal.base._Casts;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeature;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureRepositoryDefault;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureType;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
+import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionValue;
+import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRole;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -149,16 +147,6 @@ public class ApplicationPermission implements org.apache.isis.extensions.secman.
 
     private static final int TYPICAL_LENGTH_TYPE = 7;  // ApplicationFeatureType.PACKAGE is longest
 
-    // -- domain events
-
-    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationPermission, T> {}
-
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationPermission, T> {}
-
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationPermission> {}
-
-
-
     // -- identification
     /**
      * having a title() method (rather than using @Title annotation) is necessary as a workaround to be able to use
@@ -208,30 +196,14 @@ public class ApplicationPermission implements org.apache.isis.extensions.secman.
             hidden=Where.REFERENCES_PARENT
             )
     @MemberOrder(name="Role", sequence = "1")
-    @Getter @Setter
+    @Getter(onMethod = @__(@Override)) 
     private ApplicationRole role;
-
-
-
-    // -- updateRole (action)
-    public static class UpdateRoleDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = UpdateRoleDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @MemberOrder(name="Role", sequence = "1")
-    public ApplicationPermission updateRole(final ApplicationRole applicationRole) {
-        setRole(applicationRole);
-        return this;
-    }
-
-    public ApplicationRole default0UpdateRole() {
-        return getRole();
+    
+    @Override
+    public void setRole(org.apache.isis.extensions.secman.api.role.ApplicationRole applicationRole) {
+        role = _Casts.<ApplicationRole>uncheckedCast(applicationRole);
     }
 
-
-
     // -- rule (property)
     public static class RuleDomainEvent extends PropertyDomainEvent<ApplicationPermissionRule> {}
 
@@ -242,47 +214,11 @@ public class ApplicationPermission implements org.apache.isis.extensions.secman.
             editing = Editing.DISABLED
             )
     @MemberOrder(name="Permissions", sequence = "2")
-    @Getter @Setter
+    @Getter(onMethod = @__(@Override)) 
+    @Setter(onMethod = @__(@Override))
     private ApplicationPermissionRule rule;
 
 
-
-    // -- allow (action)
-    public static class AllowDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = AllowDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @MemberOrder(name = "Rule", sequence = "1")
-    public ApplicationPermission allow() {
-        setRule(ApplicationPermissionRule.ALLOW);
-        return this;
-    }
-    public String disableAllow() {
-        return getRule() == ApplicationPermissionRule.ALLOW? "Rule is already set to ALLOW": null;
-    }
-
-
-
-    // -- veto (action)
-    public static class VetoDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = VetoDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @MemberOrder(name = "Rule", sequence = "1")
-    public ApplicationPermission veto() {
-        setRule(ApplicationPermissionRule.VETO);
-        return this;
-    }
-    public String disableVeto() {
-        return getRule() == ApplicationPermissionRule.VETO? "Rule is already set to VETO": null;
-    }
-
-
-
     // -- mode (property)
     public static class ModeDomainEvent extends PropertyDomainEvent<ApplicationPermissionMode> {}
 
@@ -293,48 +229,10 @@ public class ApplicationPermission implements org.apache.isis.extensions.secman.
             editing = Editing.DISABLED
             )
     @MemberOrder(name="Permissions", sequence = "3")
-    @Getter @Setter
+    @Getter(onMethod = @__(@Override)) 
+    @Setter(onMethod = @__(@Override))
     private ApplicationPermissionMode mode;
 
-
-
-    // -- viewing(action)
-
-    public static class ViewingDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = ViewingDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @MemberOrder(name = "Mode", sequence = "1")
-    public ApplicationPermission viewing() {
-        setMode(ApplicationPermissionMode.VIEWING);
-        return this;
-    }
-    public String disableViewing() {
-        return getMode() == ApplicationPermissionMode.VIEWING ? "Mode is already set to VIEWING": null;
-    }
-
-
-
-    // -- changing (action)
-
-    public static class ChangingDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = ChangingDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @MemberOrder(name = "Mode", sequence = "2")
-    public ApplicationPermission changing() {
-        setMode(ApplicationPermissionMode.CHANGING);
-        return this;
-    }
-    public String disableChanging() {
-        return getMode() == ApplicationPermissionMode.CHANGING ? "Mode is already set to CHANGING": null;
-    }
-
-
     // -- featureId (derived property)
 
     private ApplicationFeatureId getFeatureId() {
@@ -427,23 +325,6 @@ public class ApplicationPermission implements org.apache.isis.extensions.secman.
     private String featureFqn;
 
 
-
-
-
-    // -- delete (action)
-    public static class DeleteDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE,
-            domainEvent = DeleteDomainEvent.class
-            )
-    @MemberOrder(sequence = "1")
-    public ApplicationRole delete() {
-        final ApplicationRole owningRole = getRole();
-        repository.remove(this);
-        return owningRole;
-    }
-
     // -- CONTRACT
 
     private final static ObjectContract<ApplicationPermission> contract	= 
@@ -500,4 +381,6 @@ public class ApplicationPermission implements org.apache.isis.extensions.secman.
     @Inject RepositoryService repository;
     @Inject ApplicationFeatureRepositoryDefault applicationFeatureRepository;
 
+
+
 }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRole.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRole.java
index 308a939..4f2044c 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRole.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRole.java
@@ -29,7 +29,6 @@ import javax.jdo.annotations.IdentityType;
 import javax.jdo.annotations.InheritanceStrategy;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.BookmarkPolicy;
 import org.apache.isis.applib.annotation.Bounding;
 import org.apache.isis.applib.annotation.Collection;
@@ -41,12 +40,12 @@ import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.ParameterLayout;
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.applib.services.appfeat.ApplicationMemberType;
+import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.types.DescriptionType;
 import org.apache.isis.applib.util.Equality;
@@ -54,7 +53,8 @@ import org.apache.isis.applib.util.Hashing;
 import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ToString;
 import org.apache.isis.core.commons.internal.collections._Lists;
-import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
+import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeature;
+import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureType;
 import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
@@ -62,11 +62,11 @@ import org.apache.isis.extensions.secman.jdo.dom.permission.ApplicationPermissio
 import org.apache.isis.extensions.secman.jdo.dom.permission.ApplicationPermissionRepository;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUser;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUserRepository;
-import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeature;
-import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureType;
+import org.apache.isis.extensions.secman.model.dom.permission.ApplicationPermission_delete;
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.val;
 
 @javax.jdo.annotations.PersistenceCapable(
         identityType = IdentityType.DATASTORE,
@@ -105,20 +105,6 @@ import lombok.Setter;
 public class ApplicationRole 
 implements org.apache.isis.extensions.secman.api.role.ApplicationRole, Comparable<ApplicationRole> {
 
-    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationRole, T> {}
-
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationRole, T> {}
-
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationRole> {}
-
-
-    // -- constants, moved to interface
-
-    //    public static final int MAX_LENGTH_NAME = 50;
-    //    public static final int TYPICAL_LENGTH_NAME = 30;
-    //    public static final int TYPICAL_LENGTH_DESCRIPTION = 50;
-
-
     // -- identification
     /**
      * having a title() method (rather than using @Title annotation) is necessary as a workaround to be able to use
@@ -128,7 +114,6 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRole, Comparabl
         return getName();
     }
 
-
     // -- name (property)
 
     public static class NameDomainEvent extends PropertyDomainEvent<String> {}
@@ -532,7 +517,7 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRole, Comparabl
             final ApplicationFeatureType type,
             @ParameterLayout(named="Feature", typicalLength=ApplicationFeature.TYPICAL_LENGTH_MEMBER_NAME)
             final String featureFqn) {
-        if(isAdminRole() && configBean.isStickyAdminPackage(featureFqn)) {
+        if(applicationRoleRepository.isAdminRole(this) && configBean.isStickyAdminPackage(featureFqn)) {
             return "Cannot remove top-level package permissions for the admin role.";
         }
         return null;
@@ -583,61 +568,6 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRole, Comparabl
     }
 
 
-    // -- addUser (action)
-
-    public static class AddUserDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = AddUserDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @ActionLayout(
-            named="Add")
-    @MemberOrder(name="Users", sequence = "1")
-    public ApplicationRole addUser(final ApplicationUser applicationUser) {
-        applicationUser.addRole(this);
-        // no need to add to users set, since will be done by JDO/DN.
-        return this;
-    }
-
-    public List<ApplicationUser> autoComplete0AddUser(final String search) {
-        final List<ApplicationUser> matchingSearch = applicationUserRepository.find(search);
-        final List<ApplicationUser> list = _Lists.newArrayList(matchingSearch);
-        list.removeAll(getUsers());
-        return list;
-    }
-
-
-
-    // -- removeUser (action)
-
-    public static class RemoveUserDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = RemoveUserDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @ActionLayout(
-            named="Remove"
-            )
-    @MemberOrder(name="Users", sequence = "2")
-    public ApplicationRole removeUser(final ApplicationUser applicationUser) {
-        applicationUser.removeRole(this);
-        // no need to remove from users set, since will be done by JDO/DN.
-        return this;
-    }
-
-    public java.util.Collection<ApplicationUser> choices0RemoveUser() {
-        return getUsers();
-    }
-
-    public String validateRemoveUser(
-            final ApplicationUser applicationUser) {
-        return applicationUser.validateRemoveRole(this);
-    }
-
-
-
     // -- delete (action)
     public static class DeleteDomainEvent extends ActionDomainEvent {}
 
@@ -646,28 +576,19 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRole, Comparabl
             semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE
             )
     @MemberOrder(sequence = "1")
-    public List<ApplicationRole> delete() {
+    public java.util.Collection<org.apache.isis.extensions.secman.api.role.ApplicationRole> delete() {
         getUsers().clear();
         final List<ApplicationPermission> permissions = getPermissions();
         for (final ApplicationPermission permission : permissions) {
-            permission.delete();
+            val deleteMixin = factoryService.mixin(ApplicationPermission_delete.class, permission);
+            deleteMixin.act();
         }
         repository.removeAndFlush(this);
         return applicationRoleRepository.allRoles();
     }
 
     public String disableDelete() {
-        return isAdminRole() ? "Cannot delete the admin role" : null;
-    }
-
-
-
-    // -- isAdminRole (programmatic)
-    @Programmatic
-    public boolean isAdminRole() {
-        final ApplicationRole adminRole = applicationRoleRepository.findByNameCached(
-                configBean.getAdminRoleName());
-        return this == adminRole;
+        return applicationRoleRepository.isAdminRole(this) ? "Cannot delete the admin role" : null;
     }
 
 
@@ -706,12 +627,13 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRole, Comparabl
         return toString.toString(this);
     }
 
+    @Inject FactoryService factoryService;
     @Inject RepositoryService repository;
     @Inject ApplicationFeatureRepository applicationFeatureRepository;
     @Inject ApplicationPermissionRepository applicationPermissionRepository;
     @Inject ApplicationUserRepository applicationUserRepository;
     @Inject ApplicationRoleRepository applicationRoleRepository;
-    @Inject SecurityModuleConfig configBean;
+    @Inject private SecurityModuleConfig configBean;
 
 
 }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRoleRepository.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRoleRepository.java
index 7f5d54f..6af601b 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRoleRepository.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/role/ApplicationRoleRepository.java
@@ -18,7 +18,8 @@
  */
 package org.apache.isis.extensions.secman.jdo.dom.role;
 
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -28,20 +29,24 @@ import org.springframework.stereotype.Repository;
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.query.QueryDefault;
-import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.applib.services.repository.RepositoryService;
-import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.core.commons.internal.base._Casts;
+import org.apache.isis.core.commons.internal.collections._Sets;
+import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
+import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUser;
+
+import lombok.val;
 
 @Repository
 @Named("isisExtSecman.applicationRoleRepository")
 public class ApplicationRoleRepository 
 implements org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository {
 
-    @Inject RepositoryService repository;
-    @Inject FactoryService factory;
-    @Inject QueryResultsCache queryResultsCache;
-    @Inject ApplicationRoleFactory applicationRoleFactory;
+    @Inject private RepositoryService repository;
+    @Inject private QueryResultsCache queryResultsCache;
+    @Inject private ApplicationRoleFactory applicationRoleFactory;
+    @Inject private SecurityModuleConfig configBean;
 
     @Override
     public ApplicationRole findByNameCached(final String name) {
@@ -58,12 +63,18 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository
     }
 
     @Override
-    public List<ApplicationRole> findNameContaining(final String search) {
+    public Collection<org.apache.isis.extensions.secman.api.role.ApplicationRole> findNameContaining(final String search) {
+        
         if(search != null && search.length() > 0) {
             String nameRegex = String.format("(?i).*%s.*", search.replace("*", ".*").replace("?", "."));
-            return repository.allMatches(new QueryDefault<>(ApplicationRole.class, "findByNameContaining", "nameRegex", nameRegex));
+            return repository.allMatches(
+                    new QueryDefault<>(ApplicationRole.class, 
+                            "findByNameContaining", "nameRegex", nameRegex))
+                    .stream()
+                    .map(org.apache.isis.extensions.secman.api.role.ApplicationRole.class::cast)
+                    .collect(_Sets.toUnmodifiableSorted());
         }
-        return _Lists.newArrayList();
+        return Collections.emptySortedSet();
     }
 
     @Override
@@ -81,16 +92,58 @@ implements org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository
     }
 
     @Override
-    public List<ApplicationRole> allRoles() {
-        return repository.allInstances(ApplicationRole.class);
+    public Collection<org.apache.isis.extensions.secman.api.role.ApplicationRole> allRoles() {
+        return repository.allInstances(ApplicationRole.class)
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.role.ApplicationRole.class::cast)
+                .collect(_Sets.toUnmodifiableSorted());
     }
 
     @Action(semantics = SemanticsOf.SAFE)
-    public List<ApplicationRole> findMatching(String search) {
+    public Collection<org.apache.isis.extensions.secman.api.role.ApplicationRole> findMatching(String search) {
         if (search != null && search.length() > 0 ) {
             return findNameContaining(search);
         }
-        return _Lists.newArrayList();
+        return Collections.emptySortedSet();
+    }
+    
+    @Override
+    public Collection<org.apache.isis.extensions.secman.api.user.ApplicationUser> getUsers(
+            org.apache.isis.extensions.secman.api.role.ApplicationRole genericRole) {
+        
+        val role = _Casts.<ApplicationRole>uncheckedCast(genericRole);
+        return role.getUsers()
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.user.ApplicationUser.class::cast)
+                .collect(_Sets.toUnmodifiableSorted());
+    }
+
+    @Override
+    public void addRoleToUser(
+            org.apache.isis.extensions.secman.api.role.ApplicationRole genericRole, 
+            org.apache.isis.extensions.secman.api.user.ApplicationUser genericUser) {
+        
+        val role = _Casts.<ApplicationRole>uncheckedCast(genericRole);
+        val user = _Casts.<ApplicationUser>uncheckedCast(genericUser);
+        // no need to add to users set, since will be done by JDO/DN.
+        user.addToRoles(role);
+    }
+    
+    @Override
+    public void removeRoleFromUser(
+            org.apache.isis.extensions.secman.api.role.ApplicationRole genericRole,
+            org.apache.isis.extensions.secman.api.user.ApplicationUser genericUser) {
+        
+        val role = _Casts.<ApplicationRole>uncheckedCast(genericRole);
+        val user = _Casts.<ApplicationUser>uncheckedCast(genericUser);
+        // no need to remove from users set, since will be done by JDO/DN.
+        user.removeRole(role);
+    }
+
+    @Override
+    public boolean isAdminRole(org.apache.isis.extensions.secman.api.role.ApplicationRole genericRole) {
+        final ApplicationRole adminRole = findByNameCached(configBean.getAdminRoleName());
+        return adminRole.equals(genericRole);
     }
 
 }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancy.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancy.java
index 214d139..6a90803 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancy.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancy.java
@@ -52,7 +52,6 @@ import org.apache.isis.applib.util.Equality;
 import org.apache.isis.applib.util.Hashing;
 import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ToString;
-import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUser;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUserRepository;
 
@@ -118,9 +117,6 @@ org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy {
     @Getter @Setter
     private String name;
 
-
-
-
     // -- updateName (action)
 
     public static class UpdateNameDomainEvent extends ActionDomainEvent {}
@@ -185,56 +181,6 @@ org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy {
     }
 
 
-    // -- addUser (action)
-
-    public static class AddUserDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = AddUserDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @ActionLayout(
-            named="Add"
-            )
-    @MemberOrder(name="Users", sequence = "1")
-    public ApplicationTenancy addUser(final ApplicationUser applicationUser) {
-        applicationUser.setAtPath(this.getPath());
-        // no need to add to users set, since will be done by JDO/DN.
-        return this;
-    }
-
-    public List<ApplicationUser> autoComplete0AddUser(final String search) {
-        final List<ApplicationUser> matchingSearch = applicationUserRepository.find(search);
-        final List<ApplicationUser> list = _Lists.newArrayList(matchingSearch);
-        list.removeAll(getUsers());
-        return list;
-    }
-
-
-    // -- removeUser (action)
-
-    public static class RemoveUserDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = RemoveUserDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @ActionLayout(
-            named="Remove"
-            )
-    @MemberOrder(name="Users", sequence = "2")
-    public ApplicationTenancy removeUser(final ApplicationUser applicationUser) {
-        applicationUser.setAtPath(null);
-        // no need to add to users set, since will be done by JDO/DN.
-        return this;
-    }
-    public java.util.Collection<ApplicationUser> choices0RemoveUser() {
-        return getUsers();
-    }
-    public String disableRemoveUser() {
-        return choices0RemoveUser().isEmpty()? "No users to remove": null;
-    }
-
 
 
     // -- parent (property)
@@ -354,7 +300,7 @@ org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy {
             semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE
             )
     @MemberOrder(sequence = "1")
-    public List<ApplicationTenancy> delete() {
+    public java.util.Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> delete() {
         for (final ApplicationUser user : getUsers()) {
             user.updateAtPath(null);
         }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancyRepository.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancyRepository.java
index 60600b0..5e5135b 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancyRepository.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/tenancy/ApplicationTenancyRepository.java
@@ -18,7 +18,8 @@
  */
 package org.apache.isis.extensions.secman.jdo.dom.tenancy;
 
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.concurrent.Callable;
 
 import javax.inject.Inject;
@@ -29,32 +30,43 @@ import org.springframework.stereotype.Repository;
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.query.QueryDefault;
-import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.core.commons.internal.base._Casts;
 import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.core.commons.internal.collections._Sets;
+import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUser;
+
+import lombok.val;
 
 @Repository
 @Named("isisExtSecman.applicationTenancyRepository")
 public class ApplicationTenancyRepository 
 implements org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancyRepository {
 
+    @Inject private ApplicationTenancyFactory applicationTenancyFactory;
+    @Inject private RepositoryService repository;
+    @Inject private QueryResultsCache queryResultsCache;
+    
     // -- findByNameOrPathMatching
 
     @Override
-    public List<ApplicationTenancy> findByNameOrPathMatchingCached(final String search) {
-        return queryResultsCache.execute(new Callable<List<ApplicationTenancy>>() {
-            @Override public List<ApplicationTenancy> call() throws Exception {
+    public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> findByNameOrPathMatchingCached(final String search) {
+        return queryResultsCache.execute(new Callable<Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy>>() {
+            @Override public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> call() throws Exception {
                 return findByNameOrPathMatching(search);
             }
         }, ApplicationTenancyRepository.class, "findByNameOrPathMatchingCached", search);
     }
 
-    public List<ApplicationTenancy> findByNameOrPathMatching(final String search) {
+    public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> findByNameOrPathMatching(final String search) {
         if (search == null) {
-            return _Lists.newArrayList();
+            return Collections.emptySortedSet();
         }
-        return repository.allMatches(new QueryDefault<>(ApplicationTenancy.class, "findByNameOrPathMatching", "regex", String.format("(?i).*%s.*", search.replace("*", ".*").replace("?", "."))));
+        return repository.allMatches(new QueryDefault<>(ApplicationTenancy.class, "findByNameOrPathMatching", "regex", String.format("(?i).*%s.*", search.replace("*", ".*").replace("?", "."))))
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy.class::cast)
+                .collect(_Lists.toUnmodifiable());
     }
 
     // -- findByName
@@ -95,11 +107,11 @@ implements org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancyRepos
     // -- autoComplete
 
     @Action(semantics = SemanticsOf.SAFE)
-    public List<ApplicationTenancy> findMatching(final String search) {
+    public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> findMatching(final String search) {
         if (search != null && search.length() > 0) {
             return findByNameOrPathMatching(search);
         }
-        return _Lists.newArrayList();
+        return Collections.emptySortedSet();
     }
 
 
@@ -121,27 +133,55 @@ implements org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancyRepos
         return tenancy;
     }
 
-    // -- allTenancies
+    // -- 
 
     @Override
-    public List<ApplicationTenancy> allTenancies() {
-        return queryResultsCache.execute(new Callable<List<ApplicationTenancy>>() {
+    public Collection<org.apache.isis.extensions.secman.api.user.ApplicationUser> getUsers(
+            org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy genericTenancy) {
+
+        val tenancy = _Casts.<ApplicationTenancy>uncheckedCast(genericTenancy);
+        return tenancy.getUsers()
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.user.ApplicationUser.class::cast)
+                .collect(_Sets.toUnmodifiableSorted());
+    }
+    
+    
+    @Override
+    public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> allTenancies() {
+        return queryResultsCache.execute(new Callable<Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy>>() {
             @Override
-            public List<ApplicationTenancy> call() throws Exception {
+            public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> call() throws Exception {
                 return allTenanciesNoCache();
             }
         }, ApplicationTenancyRepository.class, "allTenancies");
     }
 
-    public List<ApplicationTenancy> allTenanciesNoCache() {
-        return repository.allInstances(ApplicationTenancy.class);
+    public Collection<org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy> allTenanciesNoCache() {
+        return repository.allInstances(ApplicationTenancy.class)
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy.class::cast)
+                .collect(_Sets.toUnmodifiableSorted());
+    }
+    
+    @Override
+    public void setTenancyOnUser(
+            org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancy genericTenancy, 
+            org.apache.isis.extensions.secman.api.user.ApplicationUser genericUser) {
+        
+        val tenancy = _Casts.<ApplicationTenancy>uncheckedCast(genericTenancy);
+        val user = _Casts.<ApplicationUser>uncheckedCast(genericUser);
+        // no need to add to users set, since will be done by JDO/DN.
+        user.setAtPath(tenancy.getPath());
+    }
+    
+    @Override
+    public void clearTenancyOnUser(
+            org.apache.isis.extensions.secman.api.user.ApplicationUser genericUser) {
+        
+        val user = _Casts.<ApplicationUser>uncheckedCast(genericUser);
+        // no need to remove from users set, since will be done by JDO/DN.
+        user.setAtPath(null);
     }
-
-    // -- DEPENDENCIES
-
-    @Inject ApplicationTenancyFactory applicationTenancyFactory;
-    @Inject RepositoryService repository;
-    @Inject FactoryService factory;
-    @Inject QueryResultsCache queryResultsCache;
 
 }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUser.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUser.java
index 80426c6..c6632a9 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUser.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUser.java
@@ -55,8 +55,7 @@ import org.apache.isis.applib.util.ObjectContracts.ObjectContract;
 import org.apache.isis.applib.value.Password;
 import org.apache.isis.core.commons.internal.base._Strings;
 import org.apache.isis.core.commons.internal.collections._Lists;
-import org.apache.isis.core.commons.internal.collections._Sets;
-import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
+import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
 import org.apache.isis.extensions.secman.api.encryption.PasswordEncryptionService;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
@@ -68,7 +67,6 @@ import org.apache.isis.extensions.secman.jdo.dom.permission.ApplicationPermissio
 import org.apache.isis.extensions.secman.jdo.dom.permission.ApplicationPermissionRepository;
 import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRole;
 import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRoleRepository;
-import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureId;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -139,16 +137,6 @@ import lombok.val;
 public class ApplicationUser implements Comparable<ApplicationUser>, 
 org.apache.isis.extensions.secman.api.user.ApplicationUser {
 
-    public static abstract class PropertyDomainEvent<T>
-    extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationUser, T> {}
-
-    public static abstract class CollectionDomainEvent<T> 
-    extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationUser, T> {}
-
-    public static abstract class ActionDomainEvent 
-    extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationUser> {}
-
-
     // -- identification
 
     /**
@@ -545,7 +533,7 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
         return this;
     }
     public String disableUpdateAccountType() {
-        return isAdminUser()
+        return applicationUserRepository.isAdminUser(this)
                 ? "Cannot change account type for admin user"
                         : null;
     }
@@ -614,7 +602,7 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
         return this;
     }
     public String disableLock() {
-        if(isAdminUser()) {
+        if(applicationUserRepository.isAdminUser(this)) {
             return "Cannot disable the '" + configBean.getAdminUserName() + "' user.";
         }
         return getStatus() == ApplicationUserStatus.DISABLED ? "Status is already set to DISABLE": null;
@@ -800,35 +788,6 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
     }
 
 
-    // -- addRole (action)
-
-    public static class AddRoleDomainEvent extends ActionDomainEvent {}
-
-    @Action(
-            domainEvent = AddRoleDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT
-            )
-    @ActionLayout(
-            named="Add"
-            )
-    @MemberOrder(name="roles", sequence = "1")
-    public ApplicationUser addRole(final ApplicationRole role) {
-        addToRoles(role);
-        return this;
-    }
-
-    public SortedSet<ApplicationRole> choices0AddRole() {
-        final List<ApplicationRole> allRoles = applicationRoleRepository.allRoles();
-        final SortedSet<ApplicationRole> applicationRoles = _Sets.newTreeSet(allRoles);
-        applicationRoles.removeAll(getRoles());
-        return applicationRoles;
-    }
-
-    public String disableAddRole() {
-        return choices0AddRole().isEmpty()? "All roles added": null;
-    }
-
-
     // -- removeRole (action)
 
     public static class RemoveRoleDomainEvent extends ActionDomainEvent {}
@@ -854,9 +813,10 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
         return getRoles();
     }
 
+    // duplicated in ApplicationRole_removeUser mixin
     public String validateRemoveRole(
             final ApplicationRole applicationRole) {
-        if(isAdminUser() && applicationRole.isAdminRole()) {
+        if(applicationUserRepository.isAdminUser(this) && applicationRoleRepository.isAdminRole(applicationRole)) {
             return "Cannot remove admin user from the admin role.";
         }
         return null;
@@ -873,13 +833,13 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
             semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE
             )
     @MemberOrder(sequence = "1")
-    public List<ApplicationUser> delete() {
+    public java.util.Collection<org.apache.isis.extensions.secman.api.user.ApplicationUser> delete() {
         repository.removeAndFlush(this);
         return applicationUserRepository.allUsers();
     }
 
     public String disableDelete() {
-        return isAdminUser()? "Cannot delete the admin user": null;
+        return applicationUserRepository.isAdminUser(this)? "Cannot delete the admin user": null;
     }
 
 
@@ -901,12 +861,6 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
     }
 
 
-    // -- isAdminUser (programmatic)
-    @Programmatic
-    public boolean isAdminUser() {
-        return configBean.getAdminUserName().equals(getName());
-    }
-
     // -- helpers
     boolean isForSelfOrRunAsAdministrator() {
         return isForSelf() || isRunAsAdministrator();
@@ -975,6 +929,6 @@ org.apache.isis.extensions.secman.api.user.ApplicationUser {
      * implementation.
      */
     @Inject PermissionsEvaluationService permissionsEvaluationService;
-    @Inject SecurityModuleConfig configBean;
+    @Inject private SecurityModuleConfig configBean;
 
 }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUserRepository.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUserRepository.java
index 98c9bab..f8aea64 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUserRepository.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/dom/user/ApplicationUserRepository.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.extensions.secman.jdo.dom.user;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Callable;
 
@@ -32,7 +34,9 @@ import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.value.Password;
-import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.core.commons.internal.collections._Sets;
+import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
+import org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository;
 import org.apache.isis.extensions.secman.api.user.AccountType;
 import org.apache.isis.extensions.secman.api.user.ApplicationUserStatus;
 import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRole;
@@ -45,6 +49,8 @@ implements org.apache.isis.extensions.secman.api.user.ApplicationUserRepository
     @Inject private QueryResultsCache queryResultsCache;
     @Inject private ApplicationUserFactory applicationUserFactory;
     @Inject private RepositoryService repository;
+    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    @Inject private SecurityModuleConfig configBean;
     
     // -- findOrCreateUserByUsername (programmatic)
 
@@ -109,11 +115,14 @@ implements org.apache.isis.extensions.secman.api.user.ApplicationUserRepository
     // -- findByName
 
     @Override
-    public List<ApplicationUser> find(final String search) {
+    public Collection<org.apache.isis.extensions.secman.api.user.ApplicationUser> find(final String search) {
         final String regex = String.format("(?i).*%s.*", search.replace("*", ".*").replace("?", "."));
         return repository.allMatches(new QueryDefault<>(
                 ApplicationUser.class,
-                "find", "regex", regex));
+                "find", "regex", regex))
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.user.ApplicationUser.class::cast)
+                .collect(_Sets.toUnmodifiableSorted());
     }
 
     // -- newDelegateUser (action)
@@ -128,7 +137,7 @@ implements org.apache.isis.extensions.secman.api.user.ApplicationUserRepository
         user.setStatus(ApplicationUserStatus.parse(enabled));
         user.setAccountType(AccountType.DELEGATED);
         if (initialRole != null) {
-            user.addRole((ApplicationRole)initialRole);
+            applicationRoleRepository.addRoleToUser(initialRole, user);
         }
         repository.persist(user);
         return user;
@@ -152,7 +161,7 @@ implements org.apache.isis.extensions.secman.api.user.ApplicationUserRepository
             user.setAccountType(AccountType.LOCAL);
         }
         if (initialRole != null) {
-            user.addRole((ApplicationRole)initialRole);
+            applicationRoleRepository.addRoleToUser(initialRole, user);
         }
         if (password != null) {
             user.updatePassword(password.getPassword());
@@ -203,16 +212,19 @@ implements org.apache.isis.extensions.secman.api.user.ApplicationUserRepository
     // -- allUsers
 
     @Override
-    public List<ApplicationUser> allUsers() {
-        return repository.allInstances(ApplicationUser.class);
+    public Collection<org.apache.isis.extensions.secman.api.user.ApplicationUser> allUsers() {
+        return repository.allInstances(ApplicationUser.class)
+                .stream()
+                .map(org.apache.isis.extensions.secman.api.user.ApplicationUser.class::cast)
+                .collect(_Sets.toUnmodifiableSorted());
     }
 
     @Action(semantics = SemanticsOf.SAFE)
-    public List<ApplicationUser> findMatching(final String search) {
+    public Collection<org.apache.isis.extensions.secman.api.user.ApplicationUser> findMatching(final String search) {
         if (search != null && search.length() > 0) {
             return find(search);
         }
-        return _Lists.newArrayList();
+        return Collections.emptySortedSet();
     }
     
     // -- UPDATE USER STATE
@@ -231,6 +243,9 @@ implements org.apache.isis.extensions.secman.api.user.ApplicationUserRepository
         }
     }
 
-
+    @Override
+    public boolean isAdminUser(org.apache.isis.extensions.secman.api.user.ApplicationUser user) {
+        return configBean.getAdminUserName().equals(user.getName());
+    }
 
 }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/AbstractUserAndRolesFixtureScript.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/AbstractUserAndRolesFixtureScript.java
index 1bc2a63..055241f 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/AbstractUserAndRolesFixtureScript.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/AbstractUserAndRolesFixtureScript.java
@@ -26,12 +26,12 @@ import javax.inject.Inject;
 import org.apache.isis.applib.value.Password;
 import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
-import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
 import org.apache.isis.extensions.secman.api.user.AccountType;
 import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRole;
 import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRoleRepository;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUser;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUserRepository;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
 
 import lombok.Getter;
 
@@ -100,14 +100,11 @@ public class AbstractUserAndRolesFixtureScript extends FixtureScript {
                 final ApplicationRole securityRole = applicationRoleRepository.findByName(roleName);
 
                 if(securityRole!=null) {
-                    applicationUser.addRole(securityRole);    
+                    applicationRoleRepository.addRoleToUser(securityRole, applicationUser);
                 } else {
-
                     throw _Exceptions.unrecoverable("role not found by name: "+roleName);
-
                 }
 
-
             }
         }
     }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java
index 4e182f0..80be3de 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java
@@ -26,6 +26,7 @@ import org.apache.isis.applib.services.userreg.UserDetails;
 import org.apache.isis.applib.services.userreg.UserRegistrationService;
 import org.apache.isis.applib.value.Password;
 import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRole;
+import org.apache.isis.extensions.secman.jdo.dom.role.ApplicationRoleRepository;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUser;
 import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUserRepository;
 
@@ -35,6 +36,9 @@ import org.apache.isis.extensions.secman.jdo.dom.user.ApplicationUserRepository;
  */
 public abstract class SecurityModuleAppUserRegistrationServiceAbstract implements UserRegistrationService {
 
+    @Inject private ApplicationUserRepository applicationUserRepository;
+    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    
     @Override
     public boolean usernameExists(final String username) {
         return applicationUserRepository.findByUsername(username) != null;
@@ -54,7 +58,7 @@ public abstract class SecurityModuleAppUserRegistrationServiceAbstract implement
         final Set<ApplicationRole> additionalRoles = getAdditionalInitialRoles();
         if(additionalRoles != null) {
             for (final ApplicationRole additionalRole : additionalRoles) {
-                applicationUser.addRole(additionalRole);
+                applicationRoleRepository.addRoleToUser(additionalRole, applicationUser);
             }
         }
 
@@ -85,7 +89,5 @@ public abstract class SecurityModuleAppUserRegistrationServiceAbstract implement
      * @return Additional roles for newly created local users
      */
     protected abstract Set<ApplicationRole> getAdditionalInitialRoles();
-
-    @Inject
-    private ApplicationUserRepository applicationUserRepository;
+    
 }