Repository: gravitee-io/graviteeio-access-management
Pull request #756 (2020-04-08 12:59:18)

fix(roles): default 'owner' and 'user' roles are now defined per orga…
…nization

Closes gravitee-io/issues#3459
Closes gravitee-io/issues#3441
Closes gravitee-io/issues#3461


Patch:
diff --git a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/authentication/service/impl/AuthenticationServiceImpl.java b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/authentication/service/impl/AuthenticationServiceImpl.java
index 42f5971237..386f781d39 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/authentication/service/impl/AuthenticationServiceImpl.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/authentication/service/impl/AuthenticationServiceImpl.java
@@ -23,11 +23,9 @@ import io.gravitee.am.identityprovider.api.User;
 import io.gravitee.am.management.handlers.management.api.authentication.provider.security.EndUserAuthentication;
 import io.gravitee.am.management.handlers.management.api.authentication.provider.security.ManagementAuthenticationContext;
 import io.gravitee.am.management.handlers.management.api.authentication.service.AuthenticationService;
-import io.gravitee.am.model.Membership;
-import io.gravitee.am.model.Platform;
-import io.gravitee.am.model.ReferenceType;
-import io.gravitee.am.model.Role;
+import io.gravitee.am.model.*;
 import io.gravitee.am.model.membership.MemberType;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.service.AuditService;
 import io.gravitee.am.service.MembershipService;
@@ -146,7 +144,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
      */
     private Single<Membership> setRoles(User principal, io.gravitee.am.model.User user) {
 
-        final Maybe<Role> defaultRoleObs = roleService.findSystemRole(SystemRole.ORGANIZATION_USER, ReferenceType.ORGANIZATION);
+        final Maybe<Role> defaultRoleObs = roleService.findDefaultRole(user.getReferenceId(), DefaultRole.ORGANIZATION_USER, ReferenceType.ORGANIZATION);
         Maybe<Role> roleObs = defaultRoleObs;
 
         if (principal.getRoles() != null && !principal.getRoles().isEmpty()) {
diff --git a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/model/RoleEntity.java b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/model/RoleEntity.java
index 71457db76b..bdf17dcdb3 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/model/RoleEntity.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/model/RoleEntity.java
@@ -37,6 +37,7 @@ public class RoleEntity {
     private List<String> permissions;
     private List<String> availablePermissions;
     private boolean system;
+    private boolean defaultRole;
     private Date createdAt;
     private Date updatedAt;
 
@@ -52,6 +53,7 @@ public class RoleEntity {
         this.referenceId = other.getReferenceId();
         this.assignableType = other.getAssignableType() == null ? null : other.getAssignableType().name();
         this.system = other.isSystem();
+        this.defaultRole = other.isDefaultRole();
         this.createdAt = other.getCreatedAt();
         this.updatedAt = other.getUpdatedAt();
     }
@@ -143,4 +145,12 @@ public class RoleEntity {
     public void setAssignableType(String assignableType) {
         this.assignableType = assignableType;
     }
+
+    public boolean isDefaultRole() {
+        return defaultRole;
+    }
+
+    public void setDefaultRole(boolean defaultRole) {
+        this.defaultRole = defaultRole;
+    }
 }
diff --git a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/environments/domains/ApplicationMembersResource.java b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/environments/domains/ApplicationMembersResource.java
index 3e806901da..79f864ed35 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/environments/domains/ApplicationMembersResource.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/environments/domains/ApplicationMembersResource.java
@@ -23,6 +23,7 @@ import io.gravitee.am.model.Group;
 import io.gravitee.am.model.Membership;
 import io.gravitee.am.model.ReferenceType;
 import io.gravitee.am.model.membership.MemberType;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.Permission;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.repository.management.api.search.MembershipCriteria;
@@ -207,7 +208,7 @@ public class ApplicationMembersResource extends AbstractResource {
         }
 
         return membershipService.findByCriteria(ReferenceType.DOMAIN, domainId, criteria)
-                .switchIfEmpty(roleService.findSystemRole(SystemRole.DOMAIN_USER, ReferenceType.DOMAIN)
+                .switchIfEmpty(roleService.findDefaultRole(organizationId, DefaultRole.DOMAIN_USER, ReferenceType.DOMAIN)
                         .flatMapSingle(role -> {
                             final Membership domainMembership = new Membership();
                             domainMembership.setMemberId(newMembership.getMemberId());
diff --git a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/roles/RolesResource.java b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/roles/RolesResource.java
index 809ce894c8..87b2f7b93c 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/roles/RolesResource.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-rest/src/main/java/io/gravitee/am/management/handlers/management/api/resources/organizations/roles/RolesResource.java
@@ -106,6 +106,7 @@ public class RolesResource extends AbstractResource {
         filteredRole.setName(role.getName());
         filteredRole.setDescription(role.getDescription());
         filteredRole.setSystem(role.isSystem());
+        filteredRole.setDefaultRole(role.isDefaultRole());
         filteredRole.setAssignableType(role.getAssignableType());
 
         return filteredRole;
diff --git a/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgrader.java b/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgrader.java
index 21b09dcac7..133790fd88 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgrader.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgrader.java
@@ -67,7 +67,7 @@ public class DefaultOrganizationUpgrader implements Upgrader, Ordered {
                 // Need to create an inline provider and an admin user for this newly created default organization.
                 IdentityProvider inlineProvider = createInlineProvider();
                 User adminUser = createAdminUser(inlineProvider);
-                membershipHelper.setOrganizationAdminRole(adminUser);
+                membershipHelper.setOrganizationPrimaryOwnerRole(adminUser);
             } else {
                 logger.info("One or more organizations already exist. Check if default organization is up to date");
 
@@ -93,9 +93,9 @@ public class DefaultOrganizationUpgrader implements Upgrader, Ordered {
                             User adminUser = userService.findByUsernameAndSource(ReferenceType.ORGANIZATION, Organization.DEFAULT, ADMIN_USERNAME, inlineIdp.getId()).blockingGet();
 
                             if (adminUser == null) {
-                                // Create the admin user with organization admin role on the default organization.
+                                // Create the admin user with organization primary owner role on the default organization.
                                 adminUser = createAdminUser(inlineIdp);
-                                membershipHelper.setOrganizationAdminRole(adminUser);
+                                membershipHelper.setOrganizationPrimaryOwnerRole(adminUser);
                             }
                         }
                     }
diff --git a/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgrader.java b/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgrader.java
index b7a06912e5..f6fcec3e0f 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgrader.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgrader.java
@@ -21,13 +21,12 @@ import io.gravitee.am.model.ReferenceType;
 import io.gravitee.am.model.Role;
 import io.gravitee.am.model.User;
 import io.gravitee.am.model.common.Page;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.service.RoleService;
 import io.gravitee.am.service.UserService;
-import io.reactivex.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.Ordered;
 import org.springframework.stereotype.Component;
 
@@ -59,21 +58,21 @@ public class DefaultRoleUpgrader implements Upgrader, Ordered {
         logger.info("Applying default roles upgrade");
 
         try {
-            Boolean organizationAdminRoleNotExists = roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION).isEmpty().blockingGet();
+            Boolean organizationOwnerRoleNotExists = roleService.findDefaultRole(Organization.DEFAULT, DefaultRole.ORGANIZATION_OWNER, ReferenceType.ORGANIZATION).isEmpty().blockingGet();
             Throwable throwable = roleService.createOrUpdateSystemRoles().blockingGet();
 
             if (throwable != null) {
                 throw throwable;
-            } else if (organizationAdminRoleNotExists) {
-                Role organizationAdminRole = roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION).blockingGet();
+            } else if (organizationOwnerRoleNotExists) {
+                Role organizationOwnerRole = roleService.findDefaultRole(Organization.DEFAULT, DefaultRole.ORGANIZATION_OWNER, ReferenceType.ORGANIZATION).blockingGet();
 
-                // Must grant admin power to all existing users to be iso-functional with v2 where all users could do everything.
+                // Must grant owner power to all existing users to be iso-functional with v2 where all users could do everything.
                 Page<User> userPage;
                 int page = 0;
 
                 do {
                     userPage = userService.findAll(ReferenceType.ORGANIZATION, Organization.DEFAULT, page, PAGE_SIZE).blockingGet();
-                    userPage.getData().forEach(user -> membershipHelper.setRole(user, organizationAdminRole));
+                    userPage.getData().forEach(user -> membershipHelper.setRole(user, organizationOwnerRole));
                     page++;
                 } while (userPage.getData().size() == PAGE_SIZE);
             }
diff --git a/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelper.java b/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelper.java
index 687222fb8c..b838d39f17 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelper.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-service/src/main/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelper.java
@@ -17,6 +17,7 @@ package io.gravitee.am.management.service.impl.upgrades.helpers;
 
 import io.gravitee.am.model.*;
 import io.gravitee.am.model.membership.MemberType;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.repository.management.api.search.MembershipCriteria;
 import io.gravitee.am.service.MembershipService;
@@ -40,14 +41,14 @@ public class MembershipHelper {
     }
 
     /**
-     * Helper method to set ORGANIZATION_ADMIN role to the specified user.
+     * Helper method to set ORGANIZATION_PRIMARY_OWNER role to the specified user.
      * Note: if the user already has a role, nothing is done.
      *
      * @param user the user to define the role on.
      */
-    public void setOrganizationAdminRole(User user) {
+    public void setOrganizationPrimaryOwnerRole(User user) {
 
-        Role adminRole = roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION).blockingGet();
+        Role adminRole = roleService.findSystemRole(SystemRole.ORGANIZATION_PRIMARY_OWNER, ReferenceType.ORGANIZATION).blockingGet();
 
         setRole(user, adminRole);
     }
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/Role.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/Role.java
index bae3b88c0c..54b482517b 100644
--- a/gravitee-am-model/src/main/java/io/gravitee/am/model/Role.java
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/Role.java
@@ -36,6 +36,7 @@ public class Role {
     private String referenceId;
     private ReferenceType assignableType;
     private boolean system;
+    private boolean defaultRole;
     private Map<Permission, Set<Acl>> permissionAcls;
     private List<String> oauthScopes;
     private Date createdAt;
@@ -54,6 +55,7 @@ public class Role {
         this.permissionAcls = other.permissionAcls;
         this.oauthScopes = other.oauthScopes;
         this.system = other.system;
+        this.defaultRole = other.defaultRole;
         this.createdAt = other.createdAt;
         this.updatedAt = other.updatedAt;
     }
@@ -111,6 +113,14 @@ public class Role {
         this.system = system;
     }
 
+    public boolean isDefaultRole() {
+        return defaultRole;
+    }
+
+    public void setDefaultRole(boolean defaultRole) {
+        this.defaultRole = defaultRole;
+    }
+
     public Map<Permission, Set<Acl>> getPermissionAcls() {
         return permissionAcls;
     }
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/DefaultRole.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/DefaultRole.java
new file mode 100644
index 0000000000..3fd4ea844b
--- /dev/null
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/DefaultRole.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.am.model.permissions;
+
+/**
+ * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public enum DefaultRole {
+    ORGANIZATION_OWNER, ORGANIZATION_USER,
+    DOMAIN_OWNER, DOMAIN_USER,
+    ENVIRONMENT_OWNER,
+    APPLICATION_OWNER, APPLICATION_USER;
+}
diff --git a/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/SystemRole.java b/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/SystemRole.java
index 7a20f3cf12..2a6f2f3ef9 100644
--- a/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/SystemRole.java
+++ b/gravitee-am-model/src/main/java/io/gravitee/am/model/permissions/SystemRole.java
@@ -21,10 +21,10 @@ package io.gravitee.am.model.permissions;
  */
 public enum SystemRole {
     PLATFORM_ADMIN(true),
-    ORGANIZATION_ADMIN(false), ORGANIZATION_PRIMARY_OWNER(true), ORGANIZATION_USER(false),
-    DOMAIN_ADMIN(false), DOMAIN_PRIMARY_OWNER(true), DOMAIN_USER(false),
-    ENVIRONMENT_ADMIN(false), ENVIRONMENT_PRIMARY_OWNER(true),
-    APPLICATION_ADMIN(false), APPLICATION_PRIMARY_OWNER(true), APPLICATION_USER(false);
+    ORGANIZATION_PRIMARY_OWNER(false),
+    DOMAIN_PRIMARY_OWNER(false),
+    ENVIRONMENT_PRIMARY_OWNER(false),
+    APPLICATION_PRIMARY_OWNER(false);
 
     private boolean internalOnly;
 
diff --git a/gravitee-am-repository/gravitee-am-repository-api/src/main/java/io/gravitee/am/repository/management/api/search/MembershipCriteria.java b/gravitee-am-repository/gravitee-am-repository-api/src/main/java/io/gravitee/am/repository/management/api/search/MembershipCriteria.java
index f80ecce8b3..7796da5167 100644
--- a/gravitee-am-repository/gravitee-am-repository-api/src/main/java/io/gravitee/am/repository/management/api/search/MembershipCriteria.java
+++ b/gravitee-am-repository/gravitee-am-repository-api/src/main/java/io/gravitee/am/repository/management/api/search/MembershipCriteria.java
@@ -30,6 +30,8 @@ public class MembershipCriteria {
 
     private String userId;
 
+    private String roleId;
+
     public MembershipCriteria() {
         super();
     }
@@ -48,6 +50,12 @@ public class MembershipCriteria {
         return Optional.ofNullable(userId);
     }
 
+    public Optional<String> getRoleId() {
+
+        return Optional.ofNullable(roleId);
+    }
+
+
     public boolean isLogicalOR() {
         return logicalOR;
     }
@@ -68,4 +76,9 @@ public class MembershipCriteria {
         this.userId = userId;
         return this;
     }
+
+    public MembershipCriteria setRoleId(String roleId) {
+        this.roleId = roleId;
+        return this;
+    }
 }
diff --git a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoMembershipRepository.java b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoMembershipRepository.java
index 8578d334da..752fb258fd 100644
--- a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoMembershipRepository.java
+++ b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoMembershipRepository.java
@@ -46,6 +46,8 @@ public class MongoMembershipRepository extends AbstractManagementMongoRepository
     private static final String FIELD_REFERENCE_TYPE = "referenceType";
     private static final String FIELD_MEMBER_ID = "memberId";
     public static final String FIELD_MEMBER_TYPE = "memberType";
+    public static final String FIELD_ROLE = "role";
+
     private MongoCollection<MembershipMongo> membershipsCollection;
 
     @PostConstruct
@@ -76,6 +78,10 @@ public class MongoMembershipRepository extends AbstractManagementMongoRepository
             eqUserId = and(eq(FIELD_MEMBER_TYPE, MemberType.USER.name()), eq(FIELD_MEMBER_ID, criteria.getUserId().get()));
         }
 
+        if (criteria.getRoleId().isPresent()) {
+            eqUserId = eq(FIELD_ROLE, criteria.getRoleId().get());
+        }
+
         return toBsonFilter(criteria.isLogicalOR(), eqGroupId, eqUserId)
                 .map(filter -> and(eqReference, filter))
                 .switchIfEmpty(Single.just(eqReference))
diff --git a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoRoleRepository.java b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoRoleRepository.java
index 56dfbb586d..4919a91f0a 100644
--- a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoRoleRepository.java
+++ b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/MongoRoleRepository.java
@@ -117,6 +117,7 @@ public class MongoRoleRepository extends AbstractManagementMongoRepository imple
         role.setReferenceId(roleMongo.getReferenceId());
         role.setAssignableType(roleMongo.getAssignableType() == null ? null : ReferenceType.valueOf(roleMongo.getAssignableType()));
         role.setSystem(roleMongo.isSystem());
+        role.setDefaultRole(roleMongo.isDefaultRole());
 
         if (roleMongo.getPermissionAcls() != null) {
             Map<Permission, Set<Acl>> permissions = new HashMap<>();
@@ -150,6 +151,7 @@ public class MongoRoleRepository extends AbstractManagementMongoRepository imple
         roleMongo.setReferenceId(role.getReferenceId());
         roleMongo.setAssignableType(role.getAssignableType() == null ? null : role.getAssignableType().name());
         roleMongo.setSystem(role.isSystem());
+        roleMongo.setDefaultRole(role.isDefaultRole());
         roleMongo.setPermissionAcls(role.getPermissionAcls() == null ? null : role.getPermissionAcls().entrySet().stream().collect(Collectors.toMap(o -> o.getKey().name(), Map.Entry::getValue)));
         roleMongo.setOauthScopes(role.getOauthScopes());
         roleMongo.setCreatedAt(role.getCreatedAt());
diff --git a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/RoleMongo.java b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/RoleMongo.java
index e0aec0c992..7f9b1fde23 100644
--- a/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/RoleMongo.java
+++ b/gravitee-am-repository/gravitee-am-repository-mongodb/src/main/java/io/gravitee/am/repository/mongodb/management/internal/model/RoleMongo.java
@@ -42,6 +42,8 @@ public class RoleMongo extends Auditable {
 
     private boolean system;
 
+    private boolean defaultRole;
+
     private Map<String, Set<Acl>> permissionAcls;
 
     private List<String> oauthScopes;
@@ -132,4 +134,12 @@ public class RoleMongo extends Auditable {
     public void setOauthScopes(List<String> oauthScopes) {
         this.oauthScopes = oauthScopes;
     }
+
+    public boolean isDefaultRole() {
+        return defaultRole;
+    }
+
+    public void setDefaultRole(boolean defaultRole) {
+        this.defaultRole = defaultRole;
+    }
 }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/RoleService.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/RoleService.java
index 57e1b0d802..fb5d1ce9a5 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/RoleService.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/RoleService.java
@@ -18,6 +18,7 @@ package io.gravitee.am.service;
 import io.gravitee.am.identityprovider.api.User;
 import io.gravitee.am.model.Role;
 import io.gravitee.am.model.ReferenceType;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.service.model.NewRole;
 import io.gravitee.am.service.model.UpdateRole;
@@ -45,6 +46,8 @@ public interface RoleService {
 
     Maybe<Role> findSystemRole(SystemRole systemRole, ReferenceType assignableType);
 
+    Maybe<Role> findDefaultRole(String organizationId, DefaultRole defaultRole, ReferenceType assignableType);
+
     Single<Set<Role>> findByIdIn(List<String> ids);
 
     Single<Role> create(ReferenceType referenceType, String referenceId, NewRole newRole, User principal);
@@ -71,4 +74,5 @@ public interface RoleService {
         return delete(referenceType, referenceId, roleId, null);
     }
 
+    Completable createDefaultRoles(String organizationId);
 }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/exception/DefaultRoleUpdateException.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/exception/DefaultRoleUpdateException.java
new file mode 100644
index 0000000000..b04b44fe16
--- /dev/null
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/exception/DefaultRoleUpdateException.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.am.service.exception;
+
+import io.gravitee.common.http.HttpStatusCode;
+
+/**
+ * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public class DefaultRoleUpdateException extends AbstractManagementException {
+
+    private final String role;
+
+    public DefaultRoleUpdateException(String role) {
+        this.role = role;
+    }
+
+    @Override
+    public int getHttpStatusCode() {
+        return HttpStatusCode.BAD_REQUEST_400;
+    }
+
+    @Override
+    public String getMessage() {
+        return "Role [" + role + "] is flag as default. Its name cannot be updated.";
+    }
+}
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/exception/SinglePrimaryOwnerException.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/exception/SinglePrimaryOwnerException.java
new file mode 100644
index 0000000000..e7e55daf94
--- /dev/null
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/exception/SinglePrimaryOwnerException.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.am.service.exception;
+
+import io.gravitee.am.model.ReferenceType;
+import io.gravitee.common.http.HttpStatusCode;
+
+/**
+ * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public class SinglePrimaryOwnerException extends AbstractManagementException {
+
+    private ReferenceType type;
+
+    public SinglePrimaryOwnerException(ReferenceType type) {
+        this.type = type;
+    }
+
+    @Override
+    public String getMessage() {
+        return type.name() + " can only have one PRIMARY_OWNER !";
+    }
+
+    @Override
+    public int getHttpStatusCode() {
+        return HttpStatusCode.BAD_REQUEST_400;
+    }
+}
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/MembershipServiceImpl.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/MembershipServiceImpl.java
index e4d4a0faeb..0535d61f6c 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/MembershipServiceImpl.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/MembershipServiceImpl.java
@@ -28,6 +28,7 @@ import io.gravitee.am.model.common.event.Event;
 import io.gravitee.am.model.common.event.Payload;
 import io.gravitee.am.model.membership.Member;
 import io.gravitee.am.model.membership.MemberType;
+import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.repository.management.api.MembershipRepository;
 import io.gravitee.am.repository.management.api.search.MembershipCriteria;
 import io.gravitee.am.service.*;
@@ -39,6 +40,7 @@ import io.reactivex.Completable;
 import io.reactivex.Flowable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
+import io.reactivex.internal.operators.maybe.MaybeError;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -116,7 +118,7 @@ public class MembershipServiceImpl implements MembershipService {
         LOGGER.debug("Add or update membership {}", membership);
 
         return checkMember(organizationId, membership)
-                .andThen(checkRole(organizationId, membership.getRoleId(), membership.getReferenceType(), membership.getReferenceId()))
+                .andThen(checkRole(organizationId, membership))
                 .andThen(membershipRepository.findByReferenceAndMember(membership.getReferenceType(), membership.getReferenceId(), membership.getMemberType(), membership.getMemberId())
                         .map(Optional::of)
                         .defaultIfEmpty(Optional.empty())
@@ -244,28 +246,45 @@ public class MembershipServiceImpl implements MembershipService {
 
         if (MemberType.USER.equals(membership.getMemberType())) {
             return userService.findById(ReferenceType.ORGANIZATION, organizationId, membership.getMemberId())
-                    .toCompletable();
+                    .ignoreElement();
         } else {
             return groupService.findById(ReferenceType.ORGANIZATION, organizationId, membership.getMemberId())
-                    .toCompletable();
+                    .ignoreElement();
         }
     }
 
     /**
      * Role must exists and be of the platform roles (i.e master domain)
-     * @param role
+     * @param membership the membership to check role on.
      * @return
      */
-    private Completable checkRole(String organizationId, String role, ReferenceType referenceType, String referenceId) {
-        return roleService.findById(role)
-                .switchIfEmpty(Maybe.error(new RoleNotFoundException(role)))
+    private Completable checkRole(String organizationId, Membership membership) {
+        return roleService.findById(membership.getRoleId())
+                .switchIfEmpty(Maybe.error(new RoleNotFoundException(membership.getRoleId())))
+                .flatMap(role -> {
+                    // If role is a 'PRIMARY_OWNER' role, need to check if it is already assigned or not.
+                    if (role.isSystem() && role.getName().endsWith("_PRIMARY_OWNER")) {
+
+                        if(membership.getMemberType() == MemberType.GROUP) {
+                            return Maybe.error(new InvalidRoleException("This role cannot be assigned to a group"));
+                        }
+
+                        MembershipCriteria criteria = new MembershipCriteria();
+                        criteria.setRoleId(membership.getRoleId());
+                        return membershipRepository.findByCriteria(membership.getReferenceType(), membership.getReferenceId(), criteria)
+                                .count()
+                                .flatMapMaybe(count -> count >= 1 ? Maybe.error(new SinglePrimaryOwnerException(membership.getReferenceType())) : Maybe.just(role));
+                    }
+
+                    return Maybe.just(role);
+                })
                 // Role must be set on the right entity type.
-                .filter(role1 -> role1.getAssignableType().equals(referenceType) &&
+                .filter(role1 -> role1.getAssignableType().equals(membership.getReferenceType()) &&
                         // Role can be either a system role, either an organization role, either a domain role.
                         (role1.isSystem()
                                 || (role1.getReferenceType() == ReferenceType.ORGANIZATION && organizationId.equals(role1.getReferenceId()))
-                                || (role1.getReferenceType() == referenceType && referenceId.equals(role1.getReferenceId()))))
+                                || (role1.getReferenceType() == membership.getReferenceType() && membership.getReferenceId().equals(role1.getReferenceId()))))
                 .switchIfEmpty(Single.error(new InvalidRoleException("Invalid role")))
-                .toCompletable();
+                .ignoreElement();
     }
 }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/OrganizationServiceImpl.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/OrganizationServiceImpl.java
index f7b69e949f..a7a49a3cb7 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/OrganizationServiceImpl.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/OrganizationServiceImpl.java
@@ -21,6 +21,7 @@ import io.gravitee.am.model.Organization;
 import io.gravitee.am.repository.management.api.OrganizationRepository;
 import io.gravitee.am.service.AuditService;
 import io.gravitee.am.service.OrganizationService;
+import io.gravitee.am.service.RoleService;
 import io.gravitee.am.service.exception.OrganizationNotFoundException;
 import io.gravitee.am.service.model.NewOrganization;
 import io.gravitee.am.service.model.PatchOrganization;
@@ -37,7 +38,7 @@ import java.util.Collections;
 import java.util.Date;
 
 /**
- * @author Florent CHAMFROY (forent.chamfroy at graviteesource.com)
+ * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
  * @author GraviteeSource Team
  */
 @Component
@@ -47,11 +48,15 @@ public class OrganizationServiceImpl implements OrganizationService {
 
     private final OrganizationRepository organizationRepository;
 
+    private final RoleService roleService;
+
     private final AuditService auditService;
 
     public OrganizationServiceImpl(@Lazy OrganizationRepository organizationRepository,
+                                   RoleService roleService,
                                    AuditService auditService) {
         this.organizationRepository = organizationRepository;
+        this.roleService = roleService;
         this.auditService = auditService;
     }
 
@@ -71,7 +76,7 @@ public class OrganizationServiceImpl implements OrganizationService {
         organization.setDescription("Default organization");
         organization.setDomainRestrictions(Collections.emptyList());
 
-        // No need to create default organization of one or more organizations already exist.
+        // No need to create default organization if one or more organizations already exist.
         return organizationRepository.count()
                 .filter(aLong -> aLong == 0)
                 .flatMap(aLong -> createInternal(organization, null).toMaybe());
@@ -116,6 +121,8 @@ public class OrganizationServiceImpl implements OrganizationService {
 
         // Creates an organization and set ownership.
         return organizationRepository.create(toCreate)
+                .flatMap(createdOrganization -> roleService.createDefaultRoles(createdOrganization.getId())
+                        .andThen(Single.just(createdOrganization)))
                 .doOnSuccess(organization -> auditService.report(AuditBuilder.builder(OrganizationAuditBuilder.class).type(EventType.ORGANIZATION_CREATED).organization(organization).principal(owner)))
                 .doOnError(throwable -> auditService.report(AuditBuilder.builder(OrganizationAuditBuilder.class).type(EventType.ORGANIZATION_CREATED).organization(toCreate).principal(owner).throwable(throwable)));
     }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/RoleServiceImpl.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/RoleServiceImpl.java
index 990dd0e35e..0cd4788d90 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/RoleServiceImpl.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/impl/RoleServiceImpl.java
@@ -20,10 +20,15 @@ import io.gravitee.am.common.event.Action;
 import io.gravitee.am.common.event.Type;
 import io.gravitee.am.common.utils.RandomString;
 import io.gravitee.am.identityprovider.api.User;
-import io.gravitee.am.model.*;
+import io.gravitee.am.model.Acl;
+import io.gravitee.am.model.Platform;
+import io.gravitee.am.model.ReferenceType;
+import io.gravitee.am.model.Role;
 import io.gravitee.am.model.common.event.Event;
 import io.gravitee.am.model.common.event.Payload;
-import io.gravitee.am.model.permissions.*;
+import io.gravitee.am.model.permissions.DefaultRole;
+import io.gravitee.am.model.permissions.Permission;
+import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.repository.management.api.RoleRepository;
 import io.gravitee.am.service.AuditService;
 import io.gravitee.am.service.EventService;
@@ -33,8 +38,8 @@ import io.gravitee.am.service.model.NewRole;
 import io.gravitee.am.service.model.UpdateRole;
 import io.gravitee.am.service.reporter.builder.AuditBuilder;
 import io.gravitee.am.service.reporter.builder.management.RoleAuditBuilder;
-import io.reactivex.*;
 import io.reactivex.Observable;
+import io.reactivex.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -109,7 +114,7 @@ public class RoleServiceImpl implements RoleService {
 
     @Override
     public Maybe<Role> findSystemRole(SystemRole systemRole, ReferenceType assignableType) {
-        LOGGER.debug("Find system role : {} for the scope : {}", systemRole.name(), assignableType);
+        LOGGER.debug("Find system role : {} for the type : {}", systemRole.name(), assignableType);
         return roleRepository.findByNameAndAssignableType(ReferenceType.PLATFORM, Platform.DEFAULT, systemRole.name(), assignableType)
                 .filter(Role::isSystem)
                 .onErrorResumeNext(ex -> {
@@ -119,6 +124,18 @@ public class RoleServiceImpl implements RoleService {
                 });
     }
 
+    @Override
+    public Maybe<Role> findDefaultRole(String organizationId, DefaultRole defaultRole, ReferenceType assignableType) {
+        LOGGER.debug("Find default role {} of organization {} for the type {}", defaultRole.name(), organizationId, assignableType);
+        return roleRepository.findByNameAndAssignableType(ReferenceType.ORGANIZATION, organizationId, defaultRole.name(), assignableType)
+                .filter(Role::isDefaultRole)
+                .onErrorResumeNext(ex -> {
+                    LOGGER.error("An error occurs while trying to find default role {} of organization {} for the type {}", defaultRole.name(), organizationId, assignableType, ex);
+                    return Maybe.error(new TechnicalManagementException(
+                            String.format("An error occurs while trying to find default role %s of organization %s for type %s", defaultRole.name(), organizationId, assignableType), ex));
+                });
+    }
+
     @Override
     public Single<Set<Role>> findByIdIn(List<String> ids) {
         LOGGER.debug("Find roles by ids: {}", ids);
@@ -179,11 +196,16 @@ public class RoleServiceImpl implements RoleService {
         LOGGER.debug("Update a role {} for {} {}", id, referenceType, referenceId);
 
         return findById(referenceType, referenceId, id)
-                .map(role -> {
+                .flatMap(role -> {
                     if (role.isSystem()) {
-                        throw new SystemRoleUpdateException(id);
+                        return Single.error(new SystemRoleUpdateException(role.getName()));
                     }
-                    return role;
+
+                    if(role.isDefaultRole() && !role.getName().equals(updateRole.getName())) {
+                        return Single.error(new DefaultRoleUpdateException(role.getName()));
+                    }
+
+                    return Single.just(role);
                 })
                 .flatMap(oldRole -> {
                     // check if role name is unique
@@ -251,12 +273,22 @@ public class RoleServiceImpl implements RoleService {
     @Override
     public Completable createOrUpdateSystemRoles() {
 
-        List<Role> roles = buildAllSystemRoles();
+        List<Role> roles = buildSystemRoles();
+
+        return Observable.fromIterable(roles)
+                .flatMapCompletable(this::upsert);
+    }
+
+    @Override
+    public Completable createDefaultRoles(String organizationId) {
+
+        List<Role> roles = buildDefaultRoles(organizationId);
 
         return Observable.fromIterable(roles)
                 .flatMapCompletable(this::upsert);
     }
 
+
     private Completable upsert(Role role) {
         return roleRepository.findByNameAndAssignableType(role.getReferenceType(), role.getReferenceId(), role.getName(), role.getAssignableType())
                 .map(Optional::ofNullable)
@@ -341,11 +373,11 @@ public class RoleServiceImpl implements RoleService {
                 .filter(role -> assignableType == null || role.getAssignableType() == assignableType);
     }
 
-    private static List<Role> buildAllSystemRoles() {
+    private static List<Role> buildSystemRoles() {
 
         List<Role> roles = new ArrayList<>();
 
-        // Create PRIMARY_OWNER and ADMIN roles (we consider admin and primary owner have same permissions).
+        // Create PRIMARY_OWNER roles and PLATFORM_ADMIN role.
         Map<Permission, Set<Acl>> organizationAdminPermissions = Permission.allPermissionAcls(ReferenceType.ORGANIZATION);
         Map<Permission, Set<Acl>> platformAdminPermissions = Permission.allPermissionAcls(ReferenceType.PLATFORM);
         Map<Permission, Set<Acl>> domainAdminPermissions = Permission.allPermissionAcls(ReferenceType.DOMAIN);
@@ -355,16 +387,38 @@ public class RoleServiceImpl implements RoleService {
         organizationAdminPermissions.put(Permission.ORGANIZATION_SETTINGS, Acl.of(READ, UPDATE));
         organizationAdminPermissions.put(Permission.ORGANIZATION_AUDIT, Acl.of(READ, LIST));
 
+        domainAdminPermissions.put(Permission.DOMAIN, Acl.of(READ, UPDATE, LIST, DELETE));
         domainAdminPermissions.put(Permission.DOMAIN_SETTINGS, Acl.of(READ, UPDATE));
         domainAdminPermissions.put(Permission.DOMAIN_AUDIT, Acl.of(READ, LIST));
 
-        roles.add(buildSystemRole("PLATFORM_ADMIN", ReferenceType.PLATFORM, platformAdminPermissions));
-        roles.add(buildSystemRole("ORGANIZATION_ADMIN", ReferenceType.ORGANIZATION, organizationAdminPermissions));
-        roles.add(buildSystemRole("DOMAIN_ADMIN", ReferenceType.DOMAIN, domainAdminPermissions));
-        roles.add(buildSystemRole("APPLICATION_ADMIN", ReferenceType.APPLICATION, applicationAdminPermissions));
-        roles.add(buildSystemRole("ORGANIZATION_PRIMARY_OWNER", ReferenceType.ORGANIZATION, organizationAdminPermissions));
-        roles.add(buildSystemRole("DOMAIN_PRIMARY_OWNER", ReferenceType.DOMAIN, domainAdminPermissions));
-        roles.add(buildSystemRole("APPLICATION_PRIMARY_OWNER", ReferenceType.APPLICATION, applicationAdminPermissions));
+        roles.add(buildSystemRole(SystemRole.PLATFORM_ADMIN.name(), ReferenceType.PLATFORM, platformAdminPermissions));
+        roles.add(buildSystemRole(SystemRole.ORGANIZATION_PRIMARY_OWNER.name(), ReferenceType.ORGANIZATION, organizationAdminPermissions));
+        roles.add(buildSystemRole(SystemRole.DOMAIN_PRIMARY_OWNER.name(), ReferenceType.DOMAIN, domainAdminPermissions));
+        roles.add(buildSystemRole(SystemRole.APPLICATION_PRIMARY_OWNER.name(), ReferenceType.APPLICATION, applicationAdminPermissions));
+
+        return roles;
+    }
+
+    private List<Role> buildDefaultRoles(String organizationId) {
+
+        List<Role> roles = new ArrayList<>();
+
+        // Create OWNER and USER roles.
+        Map<Permission, Set<Acl>> organizationOwnerPermissions = Permission.allPermissionAcls(ReferenceType.ORGANIZATION);
+        Map<Permission, Set<Acl>> domainOwnerPermissions = Permission.allPermissionAcls(ReferenceType.DOMAIN);
+        Map<Permission, Set<Acl>> applicationOwnerPermissions = Permission.allPermissionAcls(ReferenceType.APPLICATION);
+
+        organizationOwnerPermissions.put(Permission.ORGANIZATION, Acl.of(READ));
+        organizationOwnerPermissions.put(Permission.ORGANIZATION_SETTINGS, Acl.of(READ, UPDATE));
+        organizationOwnerPermissions.put(Permission.ORGANIZATION_AUDIT, Acl.of(READ, LIST));
+
+        domainOwnerPermissions.put(Permission.DOMAIN, Acl.of(READ, UPDATE, LIST));
+        domainOwnerPermissions.put(Permission.DOMAIN_SETTINGS, Acl.of(READ, UPDATE));
+        domainOwnerPermissions.put(Permission.DOMAIN_AUDIT, Acl.of(READ, LIST));
+
+        roles.add(buildDefaultRole(DefaultRole.ORGANIZATION_OWNER.name(), ReferenceType.ORGANIZATION, organizationId, organizationOwnerPermissions));
+        roles.add(buildDefaultRole(DefaultRole.DOMAIN_OWNER.name(), ReferenceType.DOMAIN, organizationId, domainOwnerPermissions));
+        roles.add(buildDefaultRole(DefaultRole.APPLICATION_OWNER.name(), ReferenceType.APPLICATION, organizationId, applicationOwnerPermissions));
 
         // Create USER roles.
         Map<Permission, Set<Acl>> organizationUserPermissions = new HashMap<>();
@@ -388,22 +442,37 @@ public class RoleServiceImpl implements RoleService {
 
         applicationUserPermissions.put(Permission.APPLICATION, Acl.of(READ));
 
-        roles.add(buildSystemRole("ORGANIZATION_USER", ReferenceType.ORGANIZATION, organizationUserPermissions));
-        roles.add(buildSystemRole("DOMAIN_USER", ReferenceType.DOMAIN, domainUserPermissions));
-        roles.add(buildSystemRole("APPLICATION_USER", ReferenceType.APPLICATION, applicationUserPermissions));
+        roles.add(buildDefaultRole(DefaultRole.ORGANIZATION_USER.name(), ReferenceType.ORGANIZATION, organizationId, organizationUserPermissions));
+        roles.add(buildDefaultRole(DefaultRole.DOMAIN_USER.name(), ReferenceType.DOMAIN, organizationId, domainUserPermissions));
+        roles.add(buildDefaultRole(DefaultRole.APPLICATION_USER.name(), ReferenceType.APPLICATION, organizationId, applicationUserPermissions));
 
         return roles;
     }
 
     private static Role buildSystemRole(String name, ReferenceType assignableType, Map<Permission, Set<Acl>> permissions) {
 
+        Role systemRole = buildRole(name, assignableType, ReferenceType.PLATFORM, Platform.DEFAULT, permissions);
+        systemRole.setSystem(true);
+
+        return systemRole;
+    }
+
+    private static Role buildDefaultRole(String name, ReferenceType assignableType, String organizationId, Map<Permission, Set<Acl>> permissions) {
+
+        Role defaultRole = buildRole(name, assignableType, ReferenceType.ORGANIZATION, organizationId, permissions);
+        defaultRole.setDefaultRole(true);
+
+        return defaultRole;
+    }
+
+    private static Role buildRole(String name, ReferenceType assignableType, ReferenceType referenceType, String referenceId, Map<Permission, Set<Acl>> permissions) {
+
         Role role = new Role();
         role.setId(RandomString.generate());
         role.setName(name);
-        role.setSystem(true);
         role.setAssignableType(assignableType);
-        role.setReferenceType(ReferenceType.PLATFORM);
-        role.setReferenceId(Platform.DEFAULT);
+        role.setReferenceType(referenceType);
+        role.setReferenceId(referenceId);
         role.setPermissionAcls(permissions);
 
         return role;
diff --git a/gravitee-am-ui/src/app/app.module.ts b/gravitee-am-ui/src/app/app.module.ts
index dfd79ffdd3..15f093280f 100644
--- a/gravitee-am-ui/src/app/app.module.ts
+++ b/gravitee-am-ui/src/app/app.module.ts
@@ -104,7 +104,6 @@ import { ProvidersResolver } from "./resolvers/providers.resolver";
 import { ProviderResolver } from "./resolvers/provider.resolver";
 import { ProviderSettingsComponent } from './domain/settings/providers/provider/settings/settings.component';
 import { CreateMapperComponent, ProviderMappersComponent } from './domain/settings/providers/provider/mappers/mappers.component';
-import { Ng2BreadcrumbModule } from "libraries/ng2-breadcrumb/app.module";
 import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';
 import { CertificateCreationComponent } from './domain/settings/certificates/creation/certificate-creation.component';
 import { CertificateComponent } from './domain/settings/certificates/certificate/certificate.component';
@@ -261,6 +260,7 @@ import { FactorResolver } from "./resolvers/factor.resolver";
 import { EnrolledFactorsResolver } from "./resolvers/enrolled-factors.resolver";
 import { IdenticonHashDirective } from "./directives/identicon-hash.directive";
 import { UserAvatarComponent } from './components/user-avatar/user-avatar.component';
+import { BreadcrumbService } from './services/breadcrumb.service';
 
 @NgModule({
   declarations: [
@@ -433,11 +433,11 @@ import { UserAvatarComponent } from './components/user-avatar/user-avatar.compon
     NgxDatatableModule,
     MaterialDesignFrameworkModule,
     CodemirrorModule,
-    Ng2BreadcrumbModule.forRoot(),
     ClipboardModule,
     HighchartsChartModule
   ],
   providers: [
+    BreadcrumbService,
     DomainService,
     ProviderService,
     SidenavService,
diff --git a/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.html b/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.html
index f557ad5853..24d62200c4 100644
--- a/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.html
+++ b/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.html
@@ -15,4 +15,11 @@
     limitations under the License.
 
 -->
-<breadcrumb [useBootstrap]="false"></breadcrumb>
+<ul>
+  <li *ngFor="let url of urls; let last = last" [ngClass]="{'breadcrumb-item': true, 'active': last}"> <!-- disable link of last item -->
+    <a role="button" *ngIf="!last && url == prefix" (click)="navigateTo('/')">{{url}}</a>
+    <a role="button" *ngIf="!last && url != prefix" (click)="navigateTo(url.navigateTo)">{{url.displayUrl}}</a>
+    <span *ngIf="last">{{url.displayUrl}}</span>
+    <span *ngIf="last && url == prefix">{{friendlyName('/')}}</span>
+  </li>
+</ul>
diff --git a/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.ts b/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.ts
index 067e3c135b..74d4d1f5b6 100644
--- a/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.ts
+++ b/gravitee-am-ui/src/app/components/breadcrumb/breadcrumb.component.ts
@@ -13,18 +13,93 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit } from '@angular/core';
+import {Component, Input, OnChanges, OnDestroy, OnInit} from '@angular/core';
+import {NavigationEnd, Router} from '@angular/router';
+import {BreadcrumbService} from '../../services/breadcrumb.service';
 
 @Component({
   selector: 'gv-breadcrumb',
   templateUrl: './breadcrumb.component.html',
   styleUrls: ['./breadcrumb.component.scss']
 })
-export class BreadcrumbComponent implements OnInit {
+export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
+  @Input() prefix: string = '';
 
-  constructor() { }
+  public urls: any[];
+  public _routerSubscription: any;
+  private breadcrumbEventSubscription: any;
+  private navigateUrl;
 
-  ngOnInit() {
+  constructor(
+    private router: Router,
+    private breadcrumbService: BreadcrumbService,
+  ) {
+    this.urls = [];
+
+    // Subscribe to breadcrumb service events to be notified of route regex changes and trigger a breadcrumb regeneration.
+    this.breadcrumbEventSubscription = breadcrumbService.events.subscribe(e => {
+      if (this.navigateUrl) {
+        this.regenerateBreadcrumb(this.navigateUrl);
+      }
+    });
+
+    if (this.prefix.length > 0) {
+      this.urls.unshift(this.buildUrl(this.prefix));
+    }
+
+    this._routerSubscription = this.router.events.subscribe((navigationEnd: NavigationEnd) => {
+      if (navigationEnd instanceof NavigationEnd) {
+        this.navigateUrl = navigationEnd.urlAfterRedirects ? navigationEnd.urlAfterRedirects : navigationEnd.url;
+        this.regenerateBreadcrumb(this.navigateUrl);
+      }
+    });
+  }
+
+  ngOnInit(): void {
+  }
+
+  private regenerateBreadcrumb(url) {
+    this.urls.length = 0;
+    this.generateBreadcrumbTrail(url);
+  }
+
+  buildUrl(url): any {
+    return {displayUrl: this.friendlyName(url), navigateTo: url};
+  }
+
+  ngOnChanges(changes: any): void {
+    if (!this.urls) {
+      return;
+    }
+
+    this.urls.length = 0;
+    this.generateBreadcrumbTrail(this.router.url);
+  }
+
+  generateBreadcrumbTrail(url: string): void {
+    if (!this.breadcrumbService.isRouteHidden(url)) {
+      //Add url to beginning of array (since the url is being recursively broken down from full url to its parent)
+      this.urls.unshift(this.buildUrl(url));
+    }
+
+    if (url.lastIndexOf('/') > 0) {
+      this.generateBreadcrumbTrail(url.substr(0, url.lastIndexOf('/'))); //Find last '/' and add everything before it as a parent route
+    } else if (this.prefix.length > 0) {
+      this.urls.unshift(this.buildUrl(this.prefix));
+    }
   }
 
+  navigateTo(url: string): void {
+    this.router.navigateByUrl(url);
+  }
+
+  friendlyName(url: string): string {
+    // dummy url is never displayed in the breadcrumb to avoid blink effect.
+    return !url || url === '/dummy' ? '' : this.breadcrumbService.getFriendlyNameForRoute(url);
+  }
+
+  ngOnDestroy(): void {
+    this._routerSubscription.unsubscribe();
+    this.breadcrumbEventSubscription.unsubscribe();
+  }
 }
diff --git a/gravitee-am-ui/src/app/domain/applications/application/application.component.ts b/gravitee-am-ui/src/app/domain/applications/application/application.component.ts
index 74aa9458dc..c04b64b6b2 100644
--- a/gravitee-am-ui/src/app/domain/applications/application/application.component.ts
+++ b/gravitee-am-ui/src/app/domain/applications/application/application.component.ts
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 import {Component, OnInit} from '@angular/core';
-import {ActivatedRoute} from "@angular/router";
-import {BreadcrumbService} from "../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
+import {ActivatedRoute} from '@angular/router';
+import {AuthService} from '../../../services/auth.service';
+import {BreadcrumbService} from '../../../services/breadcrumb.service';
 import * as _ from 'lodash';
-import {AuthService} from "../../../services/auth.service";
 
 @Component({
   selector: 'app-application',
diff --git a/gravitee-am-ui/src/app/domain/applications/application/design/emails/email/email.component.ts b/gravitee-am-ui/src/app/domain/applications/application/design/emails/email/email.component.ts
index 62c66f2fd7..916c2a05cf 100644
--- a/gravitee-am-ui/src/app/domain/applications/application/design/emails/email/email.component.ts
+++ b/gravitee-am-ui/src/app/domain/applications/application/design/emails/email/email.component.ts
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit } from "@angular/core";
-import { ActivatedRoute } from "@angular/router";
-import { BreadcrumbService } from "../../../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { AuthService } from "../../../../../../services/auth.service";
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { AuthService } from '../../../../../../services/auth.service';
+import { BreadcrumbService } from '../../../../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-application-email',
diff --git a/gravitee-am-ui/src/app/domain/applications/application/design/forms/form/form.component.ts b/gravitee-am-ui/src/app/domain/applications/application/design/forms/form/form.component.ts
index 190cf359b4..730ff70f7e 100644
--- a/gravitee-am-ui/src/app/domain/applications/application/design/forms/form/form.component.ts
+++ b/gravitee-am-ui/src/app/domain/applications/application/design/forms/form/form.component.ts
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit } from "@angular/core";
-import { ActivatedRoute } from "@angular/router";
-import {BreadcrumbService} from "../../../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import {AuthService} from "../../../../../../services/auth.service";
+import {Component, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {AuthService} from '../../../../../../services/auth.service';
+import {BreadcrumbService} from '../../../../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-application-form',
diff --git a/gravitee-am-ui/src/app/domain/components/forms/form/form.component.ts b/gravitee-am-ui/src/app/domain/components/forms/form/form.component.ts
index 259f2bbb05..272d5bdaff 100644
--- a/gravitee-am-ui/src/app/domain/components/forms/form/form.component.ts
+++ b/gravitee-am-ui/src/app/domain/components/forms/form/form.component.ts
@@ -13,14 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {AfterViewInit, Component, ElementRef, Inject, Input, OnInit, ViewChild} from "@angular/core";
-import { ActivatedRoute, Router } from "@angular/router";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { AppConfig } from "../../../../../config/app.config";
-import { FormService } from "../../../../services/form.service";
-import { SnackbarService } from "../../../../services/snackbar.service";
-import { DialogService } from "../../../../services/dialog.service";
-import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material";
+import { AfterViewInit, Component, ElementRef, Inject, Input, OnInit, ViewChild } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material';
+import { ActivatedRoute, Router } from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
+import { FormService } from '../../../../services/form.service';
+import { SnackbarService } from '../../../../services/snackbar.service';
+import { DialogService } from '../../../../services/dialog.service';
 
 export interface DialogData {
   rawTemplate: string;
diff --git a/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.html b/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.html
index 4480c1e924..78829e0bed 100644
--- a/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.html
+++ b/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.html
@@ -51,7 +51,7 @@
       </mat-form-field>
       <mat-form-field appearance="outline" floatLabel="always" fxFlex="20">
         <mat-select placeholder="Role" name="userRole" [(ngModel)]="selectedRole">
-          <mat-option *ngFor="let role of roles" [value]="role.id">
+          <mat-option *ngFor="let role of roles" [value]="role.id" [disabled]="isRoleDisabled(role)">
             <span>{{ role.name }}</span>
           </mat-option>
         </mat-select>
@@ -94,7 +94,7 @@
       <ng-template let-row="row" ngx-datatable-cell-template>
         <mat-form-field *ngIf="isEditable(row)" style="margin-top: -12px;">
           <mat-select name="memberRole" [value]="row.roleId" (selectionChange)="update(row.memberId, row.memberType, $event)" [disabled]="!editMode">
-            <mat-option *ngFor="let role of roles" [value]="role.id">
+            <mat-option *ngFor="let role of roles" [value]="role.id" [disabled]="isRoleDisabled(role)">
               <span>{{ role.name }}</span>
             </mat-option>
           </mat-select>
diff --git a/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.ts b/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.ts
index 9bd7d510b6..98e7926a0b 100644
--- a/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.ts
+++ b/gravitee-am-ui/src/app/domain/components/memberships/memberships.component.ts
@@ -131,6 +131,10 @@ export class MembershipsComponent implements OnInit, OnChanges {
     return membership.roleName && membership.roleName.endsWith('_PRIMARY_OWNER');
   }
 
+  isRoleDisabled(role) {
+    return role.name.endsWith('_PRIMARY_OWNER') && role.system === true;
+  }
+
   isMySelf(membership) {
     return this.authService.user().sub === membership.memberId && membership.memberType === 'user';
   }
diff --git a/gravitee-am-ui/src/app/domain/creation/domain-creation.component.ts b/gravitee-am-ui/src/app/domain/creation/domain-creation.component.ts
index 627fa64525..642b07131a 100644
--- a/gravitee-am-ui/src/app/domain/creation/domain-creation.component.ts
+++ b/gravitee-am-ui/src/app/domain/creation/domain-creation.component.ts
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 import { Component, OnInit } from '@angular/core';
-import { DomainService } from "../../services/domain.service";
-import { SnackbarService } from "../../services/snackbar.service";
-import { Router } from "@angular/router";
-import { BreadcrumbService } from "../../../libraries/ng2-breadcrumb/components/breadcrumbService";
+import { Router } from '@angular/router';
+import { DomainService } from '../../services/domain.service';
+import { SnackbarService } from '../../services/snackbar.service';
+import { BreadcrumbService } from '../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-creation',
diff --git a/gravitee-am-ui/src/app/domain/domain.component.ts b/gravitee-am-ui/src/app/domain/domain.component.ts
index 3c8a0471af..36524cccd4 100644
--- a/gravitee-am-ui/src/app/domain/domain.component.ts
+++ b/gravitee-am-ui/src/app/domain/domain.component.ts
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 import {Component, OnInit} from '@angular/core';
-import {ActivatedRoute, Router} from "@angular/router";
-import {SidenavService} from "../components/sidenav/sidenav.service";
-import {BreadcrumbService} from "../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import {DomainService} from "../services/domain.service";
-import {NavbarService} from "../components/navbar/navbar.service";
-import {SnackbarService} from "../services/snackbar.service";
-import {AuthService} from "../services/auth.service";
+import {ActivatedRoute, Router} from '@angular/router';
+import {SidenavService} from '../components/sidenav/sidenav.service';
+import {BreadcrumbService} from '../services/breadcrumb.service';
+import {DomainService} from '../services/domain.service';
+import {NavbarService} from '../components/navbar/navbar.service';
+import {SnackbarService} from '../services/snackbar.service';
+import {AuthService} from '../services/auth.service';
 
 @Component({
   selector: 'app-domain',
diff --git a/gravitee-am-ui/src/app/domain/settings/audits/settings/reporter/reporter.component.ts b/gravitee-am-ui/src/app/domain/settings/audits/settings/reporter/reporter.component.ts
index c684f0ab4d..4c16a5e2cd 100644
--- a/gravitee-am-ui/src/app/domain/settings/audits/settings/reporter/reporter.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/audits/settings/reporter/reporter.component.ts
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 import { Component, OnInit, ViewChild } from '@angular/core';
-import { ActivatedRoute, Router } from "@angular/router";
-import { AppConfig } from "../../../../../../config/app.config";
-import { OrganizationService } from "../../../../../services/organization.service";
-import { ReporterService } from "../../../../../services/reporter.service";
-import { BreadcrumbService } from "../../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { SnackbarService } from "../../../../../services/snackbar.service";
+import { ActivatedRoute, Router } from '@angular/router';
+import { OrganizationService } from '../../../../../services/organization.service';
+import { ReporterService } from '../../../../../services/reporter.service';
+import { BreadcrumbService } from '../../../../../services/breadcrumb.service';
+import { SnackbarService } from '../../../../../services/snackbar.service';
 
 @Component({
   selector: 'app-reporter',
diff --git a/gravitee-am-ui/src/app/domain/settings/certificates/certificate/certificate.component.ts b/gravitee-am-ui/src/app/domain/settings/certificates/certificate/certificate.component.ts
index ebcf88a8da..8add8d8c7a 100644
--- a/gravitee-am-ui/src/app/domain/settings/certificates/certificate/certificate.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/certificates/certificate/certificate.component.ts
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute, Router } from "@angular/router";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { OrganizationService } from "../../../../services/organization.service";
-import { CertificateService}  from "../../../../services/certificate.service";
-import { SnackbarService } from "../../../../services/snackbar.service";
-import { DialogService } from "../../../../services/dialog.service";
+import { ActivatedRoute, Router } from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
+import { OrganizationService } from '../../../../services/organization.service';
+import { CertificateService}  from '../../../../services/certificate.service';
+import { SnackbarService } from '../../../../services/snackbar.service';
+import { DialogService } from '../../../../services/dialog.service';
 
 @Component({
   selector: 'app-certificate',
diff --git a/gravitee-am-ui/src/app/domain/settings/emails/email/email.component.ts b/gravitee-am-ui/src/app/domain/settings/emails/email/email.component.ts
index f062afbbdb..f37b596476 100644
--- a/gravitee-am-ui/src/app/domain/settings/emails/email/email.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/emails/email/email.component.ts
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit } from "@angular/core";
-import { ActivatedRoute } from "@angular/router";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import {AuthService} from "../../../../services/auth.service";
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
+import { AuthService } from '../../../../services/auth.service';
 
 @Component({
   selector: 'app-domain-email',
diff --git a/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grant/extension-grant.component.ts b/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grant/extension-grant.component.ts
index e9da867e12..ac5ebaf5ab 100644
--- a/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grant/extension-grant.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grant/extension-grant.component.ts
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 import { Component, OnInit } from '@angular/core';
-import {ActivatedRoute, Router} from "@angular/router";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { OrganizationService } from "../../../../services/organization.service";
-import { SnackbarService } from "../../../../services/snackbar.service";
-import { ExtensionGrantService } from "../../../../services/extension-grant.service";
-import {DialogService} from "../../../../services/dialog.service";
-import {AuthService} from "../../../../services/auth.service";
+import {ActivatedRoute, Router} from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
+import { OrganizationService } from '../../../../services/organization.service';
+import { SnackbarService } from '../../../../services/snackbar.service';
+import { ExtensionGrantService } from '../../../../services/extension-grant.service';
+import {DialogService} from '../../../../services/dialog.service';
+import {AuthService} from '../../../../services/auth.service';
 
 @Component({
   selector: 'app-extension-grant',
diff --git a/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grants.component.ts b/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grants.component.ts
index 247781eeb0..bc513819c4 100644
--- a/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grants.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/extension-grants/extension-grants.component.ts
@@ -18,7 +18,7 @@ import {ActivatedRoute} from '@angular/router';
 import {DialogService} from 'app/services/dialog.service';
 import {SnackbarService} from '../../../services/snackbar.service';
 import {ExtensionGrantService} from '../../../services/extension-grant.service';
-import {BreadcrumbService} from "../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
+import {BreadcrumbService} from '../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-extension-grants',
diff --git a/gravitee-am-ui/src/app/domain/settings/factors/factor/factor.component.ts b/gravitee-am-ui/src/app/domain/settings/factors/factor/factor.component.ts
index fe5c953cf4..92e187fd6e 100644
--- a/gravitee-am-ui/src/app/domain/settings/factors/factor/factor.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/factors/factor/factor.component.ts
@@ -15,7 +15,7 @@
  */
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router} from '@angular/router';
-import { BreadcrumbService } from '../../../../../libraries/ng2-breadcrumb/components/breadcrumbService';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
 import { OrganizationService } from '../../../../services/organization.service';
 import { SnackbarService } from '../../../../services/snackbar.service';
 import { DialogService } from '../../../../services/dialog.service';
diff --git a/gravitee-am-ui/src/app/domain/settings/general/general.component.ts b/gravitee-am-ui/src/app/domain/settings/general/general.component.ts
index 356e3857c4..90ea670bf6 100644
--- a/gravitee-am-ui/src/app/domain/settings/general/general.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/general/general.component.ts
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 import {Component, OnInit, ViewChild} from '@angular/core';
-import {MatInput} from "@angular/material";
-import {ActivatedRoute, Router} from "@angular/router";
-import {DomainService} from "../../../services/domain.service";
-import {DialogService} from "../../../services/dialog.service";
-import {SnackbarService} from "../../../services/snackbar.service";
-import {BreadcrumbService} from "../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import {AuthService} from "../../../services/auth.service";
-import * as _ from "lodash";
-import {NavbarService} from "../../../components/navbar/navbar.service";
+import {MatInput} from '@angular/material';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DomainService} from '../../../services/domain.service';
+import {DialogService} from '../../../services/dialog.service';
+import {SnackbarService} from '../../../services/snackbar.service';
+import {BreadcrumbService} from '../../../services/breadcrumb.service';
+import {AuthService} from '../../../services/auth.service';
+import {NavbarService} from '../../../components/navbar/navbar.service';
+import * as _ from 'lodash';
 
 export interface Tag {
   id: string;
diff --git a/gravitee-am-ui/src/app/domain/settings/groups/group/group.component.ts b/gravitee-am-ui/src/app/domain/settings/groups/group/group.component.ts
index 6baa1dde8f..1d076239b3 100644
--- a/gravitee-am-ui/src/app/domain/settings/groups/group/group.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/groups/group/group.component.ts
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 import { Component, OnInit } from '@angular/core';
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { ActivatedRoute, Router } from "@angular/router";
-import { AppConfig } from "../../../../../config/app.config";
+import { ActivatedRoute, Router } from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-group',
diff --git a/gravitee-am-ui/src/app/domain/settings/groups/group/settings/settings.component.ts b/gravitee-am-ui/src/app/domain/settings/groups/group/settings/settings.component.ts
index 08396189d8..5f7e60744a 100644
--- a/gravitee-am-ui/src/app/domain/settings/groups/group/settings/settings.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/groups/group/settings/settings.component.ts
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 import {Component, OnInit, ViewChild} from '@angular/core';
-import { ActivatedRoute, Router } from "@angular/router";
-import { BreadcrumbService } from "../../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { SnackbarService } from "../../../../../services/snackbar.service";
-import { DialogService } from "../../../../../services/dialog.service";
-import { GroupService } from "../../../../../services/group.service";
-import {AuthService} from "../../../../../services/auth.service";
+import { ActivatedRoute, Router } from '@angular/router';
+import { BreadcrumbService } from '../../../../../services/breadcrumb.service';
+import { SnackbarService } from '../../../../../services/snackbar.service';
+import { DialogService } from '../../../../../services/dialog.service';
+import { GroupService } from '../../../../../services/group.service';
+import {AuthService} from '../../../../../services/auth.service';
 
 @Component({
   selector: 'app-group-settings',
diff --git a/gravitee-am-ui/src/app/domain/settings/openid/client-registration/client-registration.component.ts b/gravitee-am-ui/src/app/domain/settings/openid/client-registration/client-registration.component.ts
index 25f8755b0a..2b15ed0398 100644
--- a/gravitee-am-ui/src/app/domain/settings/openid/client-registration/client-registration.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/openid/client-registration/client-registration.component.ts
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 import {Component, OnInit} from '@angular/core';
-import { ActivatedRoute } from "@angular/router";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
+import { ActivatedRoute } from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-openid-client-registration',
diff --git a/gravitee-am-ui/src/app/domain/settings/providers/provider/provider.component.ts b/gravitee-am-ui/src/app/domain/settings/providers/provider/provider.component.ts
index 8c0ee145d2..6f2598372c 100644
--- a/gravitee-am-ui/src/app/domain/settings/providers/provider/provider.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/providers/provider/provider.component.ts
@@ -15,7 +15,7 @@
  */
 import {Component, OnInit} from '@angular/core';
 import {ActivatedRoute, Router} from '@angular/router';
-import {BreadcrumbService} from '../../../../../libraries/ng2-breadcrumb/components/breadcrumbService';
+import {BreadcrumbService} from '../../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-provider',
diff --git a/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/create/create.component.html b/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/create/create.component.html
index d4b69a1323..20b83e4351 100644
--- a/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/create/create.component.html
+++ b/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/create/create.component.html
@@ -29,7 +29,7 @@
     <h5 style="color: grey; margin:  5px 0 15px 0;">Roles</h5>
     <mat-form-field>
       <mat-select placeholder="Roles" name="roles" ngModel [multiple]="!data.organizationContext" required>
-        <mat-option *ngFor="let role of data.roles" [value]="role.id">{{ role.name }}</mat-option>
+        <mat-option *ngFor="let role of data.roles" [value]="role.id" [disabled]="role.name.endsWith('_PRIMARY_OWNER') && role.system === true">{{ role.name }}</mat-option>
       </mat-select>
     </mat-form-field>
   </form>
diff --git a/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/roles.component.ts b/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/roles.component.ts
index e4df8bc086..a645beb5b2 100644
--- a/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/roles.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/providers/provider/roles/roles.component.ts
@@ -165,4 +165,6 @@ export class CreateRoleMapperComponent {
     let formValue = this.form.value;
     return !formValue.user;
   }
+
+
 }
diff --git a/gravitee-am-ui/src/app/domain/settings/providers/provider/settings/settings.component.ts b/gravitee-am-ui/src/app/domain/settings/providers/provider/settings/settings.component.ts
index 013c3a7020..ef8225f483 100644
--- a/gravitee-am-ui/src/app/domain/settings/providers/provider/settings/settings.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/providers/provider/settings/settings.component.ts
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 import { Component, OnInit, ViewChild } from '@angular/core';
-import { ProviderService } from "../../../../../services/provider.service";
-import { SnackbarService } from "../../../../../services/snackbar.service";
-import { ActivatedRoute, Router } from "@angular/router";
-import { OrganizationService } from "../../../../../services/organization.service";
-import { BreadcrumbService } from "../../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { DomainService } from "../../../../../services/domain.service";
-import { AppConfig } from "../../../../../../config/app.config";
-import { NgForm } from "@angular/forms";
-import { DialogService } from "../../../../../services/dialog.service";
+import { NgForm } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ProviderService } from '../../../../../services/provider.service';
+import { SnackbarService } from '../../../../../services/snackbar.service';
+import { OrganizationService } from '../../../../../services/organization.service';
+import { BreadcrumbService } from '../../../../../services/breadcrumb.service';
+import { DomainService } from '../../../../../services/domain.service';
+import { DialogService } from '../../../../../services/dialog.service';
 import * as _ from 'lodash';
 
 @Component({
diff --git a/gravitee-am-ui/src/app/domain/settings/roles/role/role.component.ts b/gravitee-am-ui/src/app/domain/settings/roles/role/role.component.ts
index e22d5c45dd..ca51e694c6 100644
--- a/gravitee-am-ui/src/app/domain/settings/roles/role/role.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/roles/role/role.component.ts
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit, ViewChild } from "@angular/core";
-import { ActivatedRoute, Router } from "@angular/router";
-import { RoleService } from "../../../../services/role.service";
-import { SnackbarService } from "../../../../services/snackbar.service";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import { MatInput } from "@angular/material/input";
-import * as _ from "lodash";
-import { DialogService } from "../../../../services/dialog.service";
-import {AuthService} from "../../../../services/auth.service";
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { MatInput } from '@angular/material/input';
+import { RoleService } from '../../../../services/role.service';
+import { SnackbarService } from '../../../../services/snackbar.service';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
+import { DialogService } from '../../../../services/dialog.service';
+import { AuthService } from '../../../../services/auth.service';
+import * as _ from 'lodash';
 
 export interface Scope {
   id: string;
diff --git a/gravitee-am-ui/src/app/domain/settings/scopes/scope/scope.component.ts b/gravitee-am-ui/src/app/domain/settings/scopes/scope/scope.component.ts
index 3dff8d7c3f..bdec3e6ecd 100644
--- a/gravitee-am-ui/src/app/domain/settings/scopes/scope/scope.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/scopes/scope/scope.component.ts
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 import { Component, OnInit, ViewChild } from '@angular/core';
-import { ActivatedRoute, Router } from "@angular/router";
-import { ScopeService } from "../../../../services/scope.service";
-import { SnackbarService } from "../../../../services/snackbar.service";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import * as moment from "moment";
-import { NgForm } from "@angular/forms";
-import { DialogService } from "../../../../services/dialog.service";
-import {AuthService} from "../../../../services/auth.service";
+import { NgForm } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ScopeService } from '../../../../services/scope.service';
+import { SnackbarService } from '../../../../services/snackbar.service';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
+import { DialogService } from '../../../../services/dialog.service';
+import { AuthService } from '../../../../services/auth.service';
+import * as moment from 'moment';
 
 @Component({
   selector: 'app-scope',
diff --git a/gravitee-am-ui/src/app/domain/settings/users/user/user.component.ts b/gravitee-am-ui/src/app/domain/settings/users/user/user.component.ts
index c335ef8203..17ccd48896 100644
--- a/gravitee-am-ui/src/app/domain/settings/users/user/user.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/users/user/user.component.ts
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 import { Component, OnInit } from '@angular/core';
-import {ActivatedRoute, Router} from "@angular/router";
-import { BreadcrumbService } from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
+import {ActivatedRoute, Router} from '@angular/router';
+import { BreadcrumbService } from '../../../../services/breadcrumb.service';
 
 @Component({
   selector: 'app-user',
diff --git a/gravitee-am-ui/src/app/services/breadcrumb.service.ts b/gravitee-am-ui/src/app/services/breadcrumb.service.ts
new file mode 100644
index 0000000000..1cb284311f
--- /dev/null
+++ b/gravitee-am-ui/src/app/services/breadcrumb.service.ts
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Subject} from 'rxjs';
+
+@Injectable()
+export class BreadcrumbService {
+
+  private routesFriendlyNames: Map<string, string> = new Map<string, string>();
+  private routesFriendlyNamesRegex: Map<string, string> = new Map<string, string>();
+  private routesWithCallback: Map<string, (string: string) => string> = new Map<string, (string: string) => string>();
+  private routesWithCallbackRegex: Map<string, (string: string) => string> = new Map<string, (string: string) => string>();
+  private hideRoutes: any = new Array<string>();
+  private hideRoutesRegex: any = new Array<string>();
+
+  public events: Subject<any> = new Subject<any>();
+
+  /**
+   * Specify a friendly name for the corresponding route.
+   *
+   * @param route
+   * @param name
+   */
+  addFriendlyNameForRoute(route: string, name: string): void {
+    this.routesFriendlyNames.set(route, name);
+    this.notify();
+  }
+
+  /**
+   * Specify a friendly name for the corresponding route matching a regular expression.
+   *
+   * @param route
+   * @param name
+   */
+  addFriendlyNameForRouteRegex(routeRegex: string, name: string): void {
+    this.routesFriendlyNamesRegex.set(routeRegex, name);
+    this.notify();
+  }
+
+  /**
+   * Specify a callback for the corresponding route.
+   * When a mathing url is navigatedd to, the callback function is invoked to get the name to be displayed in the breadcrumb.
+   */
+  addCallbackForRoute(route: string, callback: (id: string) => string): void {
+    this.routesWithCallback.set(route, callback);
+    this.notify();
+  }
+
+  /**
+   * Specify a callback for the corresponding route matching a regular expression.
+   * When a mathing url is navigatedd to, the callback function is invoked to get the name to be displayed in the breadcrumb.
+   */
+  addCallbackForRouteRegex(routeRegex: string, callback: (id: string) => string): void {
+    this.routesWithCallbackRegex.set(routeRegex, callback);
+    this.notify();
+  }
+
+  /**
+   * Show the friendly name for a given route (url). If no match is found the url (without the leading '/') is shown.
+   *
+   * @param route
+   * @returns {*}
+   */
+  getFriendlyNameForRoute(route: string): string {
+    let name: string;
+    const routeEnd = route.substr(route.lastIndexOf('/') + 1, route.length);
+
+    this.routesFriendlyNames.forEach((value, key, map) => {
+      if (key === route) {
+        name = value;
+      }
+    });
+
+    this.routesFriendlyNamesRegex.forEach((value, key, map) => {
+      if (new RegExp(key).exec(route)) {
+        name = value;
+      }
+    });
+
+    this.routesWithCallback.forEach((value, key, map) => {
+      if (key === route) {
+        name = value(routeEnd);
+      }
+    });
+
+    this.routesWithCallbackRegex.forEach((value, key, map) => {
+      if (new RegExp(key).exec(route)) {
+        name = value(routeEnd);
+      }
+    });
+
+    return name ? name : routeEnd;
+  }
+
+  /**
+   * Specify a route (url) that should not be shown in the breadcrumb.
+   */
+  hideRoute(route: string): void {
+    if (this.hideRoutes.indexOf(route) === -1) {
+      this.hideRoutes.push(route);
+      this.notify();
+    }
+  }
+
+  /**
+   * Specify a route (url) regular expression that should not be shown in the breadcrumb.
+   */
+  hideRouteRegex(routeRegex: string): void {
+    if (this.hideRoutesRegex.indexOf(routeRegex) === -1) {
+      this.hideRoutesRegex.push(routeRegex);
+      this.notify();
+    }
+  }
+
+  /**
+   * Returns true if a route should be hidden.
+   */
+  isRouteHidden(route: string): boolean {
+    let hide = this.hideRoutes.indexOf(route) > -1;
+
+    this.hideRoutesRegex.forEach((value: any) => {
+      if (new RegExp(value).exec(route)) {
+        hide = true;
+      }
+    });
+
+    return hide;
+  }
+
+  notify() {
+    this.events.next('refresh');
+  }
+}
diff --git a/gravitee-am-ui/src/app/settings/management/roles/role/role.component.html b/gravitee-am-ui/src/app/settings/management/roles/role/role.component.html
index 7d9f6ee28a..c9b7b0f649 100644
--- a/gravitee-am-ui/src/app/settings/management/roles/role/role.component.html
+++ b/gravitee-am-ui/src/app/settings/management/roles/role/role.component.html
@@ -24,7 +24,7 @@
         <div class="gv-form-section">
           <mat-form-field appearance="outline" floatLabel="always">
             <mat-label>Name</mat-label>
-            <input matInput type="text" placeholder="Name" name="name" [(ngModel)]="role.name" required [readonly]="readonly">
+            <input matInput type="text" placeholder="Name" name="name" [(ngModel)]="role.name" required [readonly]="readonly" [disabled]="role.system || role.defaultRole">
             <mat-hint>A name for your role.</mat-hint>
           </mat-form-field>
 
diff --git a/gravitee-am-ui/src/app/settings/management/roles/role/role.component.ts b/gravitee-am-ui/src/app/settings/management/roles/role/role.component.ts
index 04cace8496..6d063e5fd5 100644
--- a/gravitee-am-ui/src/app/settings/management/roles/role/role.component.ts
+++ b/gravitee-am-ui/src/app/settings/management/roles/role/role.component.ts
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 import {Component, OnInit} from '@angular/core';
-import {AppConfig} from "../../../../../config/app.config";
-import {RoleService} from "../../../../services/role.service";
-import {SnackbarService} from "../../../../services/snackbar.service";
-import {ActivatedRoute, Router} from "@angular/router";
-import {BreadcrumbService} from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import {DialogService} from "../../../../services/dialog.service";
-import {OrganizationService} from "../../../../services/organization.service";
-import {AuthService} from "../../../../services/auth.service";
+import {ActivatedRoute, Router} from '@angular/router';
+import {SnackbarService} from '../../../../services/snackbar.service';
+import {BreadcrumbService} from '../../../../services/breadcrumb.service';
+import {DialogService} from '../../../../services/dialog.service';
+import {OrganizationService} from '../../../../services/organization.service';
+import {AuthService} from '../../../../services/auth.service';
 
 @Component({
   selector: 'app-settings-management-role',
@@ -54,6 +52,7 @@ export class ManagementRoleComponent implements OnInit {
     this.readonly = this.role.system || (!this.authService.hasPermissions(['organization_role_update']));
     this.deleteMode = this.authService.hasPermissions(['organization_role_delete']);
     this.initPermissions();
+    this.initBreadcrumb();
   }
 
   update() {
@@ -146,4 +145,8 @@ export class ManagementRoleComponent implements OnInit {
     }
     return array;
   }
+
+  initBreadcrumb() {
+    this.breadcrumbService.addFriendlyNameForRouteRegex('/settings/management/roles/'+this.role.id+'(:?.*)?$', this.role.name.toLowerCase());
+  }
 }
diff --git a/gravitee-am-ui/src/app/settings/management/roles/roles.component.html b/gravitee-am-ui/src/app/settings/management/roles/roles.component.html
index f87dc90b99..27637ac8ce 100644
--- a/gravitee-am-ui/src/app/settings/management/roles/roles.component.html
+++ b/gravitee-am-ui/src/app/settings/management/roles/roles.component.html
@@ -53,6 +53,7 @@
               <div fxLayout="row">
                 <a [routerLink]="[row.id]" [queryParams]="{system:row.system}">{{row.name}}</a>
                 <mat-chip-list *ngIf="row.system"><mat-chip>system</mat-chip></mat-chip-list>
+                <mat-chip-list *ngIf="row.defaultRole"><mat-chip class="mat-default-chip">default</mat-chip></mat-chip-list>
               </div>
               <small>{{row.description}}</small>
             </div>
diff --git a/gravitee-am-ui/src/app/settings/management/tags/tag/tag.component.ts b/gravitee-am-ui/src/app/settings/management/tags/tag/tag.component.ts
index 4a66826c8a..401c0a15bd 100644
--- a/gravitee-am-ui/src/app/settings/management/tags/tag/tag.component.ts
+++ b/gravitee-am-ui/src/app/settings/management/tags/tag/tag.component.ts
@@ -13,14 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {Component, OnInit, ViewChild} from "@angular/core";
-import {ActivatedRoute, Router} from "@angular/router";
-import {SnackbarService} from "../../../../services/snackbar.service";
-import {BreadcrumbService} from "../../../../../libraries/ng2-breadcrumb/components/breadcrumbService";
-import {TagService} from "../../../../services/tag.service";
-import {NgForm} from "@angular/forms";
-import {DialogService} from "../../../../services/dialog.service";
-import {AuthService} from "../../../../services/auth.service";
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {NgForm} from '@angular/forms';
+import {ActivatedRoute, Router} from '@angular/router';
+import {SnackbarService} from '../../../../services/snackbar.service';
+import {BreadcrumbService} from '../../../../services/breadcrumb.service';
+import {TagService} from '../../../../services/tag.service';
+import {DialogService} from '../../../../services/dialog.service';
+import {AuthService} from '../../../../services/auth.service';
 
 @Component({
   selector: 'app-tag',
diff --git a/gravitee-am-ui/src/libraries/ng2-breadcrumb/app.module.ts b/gravitee-am-ui/src/libraries/ng2-breadcrumb/app.module.ts
deleted file mode 100644
index 880d40f242..0000000000
--- a/gravitee-am-ui/src/libraries/ng2-breadcrumb/app.module.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { NgModule, ModuleWithProviders  }      from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-import { BreadcrumbComponent } from './components/breadcrumb';
-import { BreadcrumbService } from './components/breadcrumbService';
-
-export * from './components/breadcrumb'
-export * from './components/breadcrumbService'
-
-@NgModule({
-    imports: [
-        CommonModule
-    ],
-    declarations: [
-        BreadcrumbComponent
-    ],
-    exports: [
-        BreadcrumbComponent
-    ]
-})
-export class Ng2BreadcrumbModule {
-    static forRoot(): ModuleWithProviders {
-        return {
-            ngModule: Ng2BreadcrumbModule,
-            providers: [BreadcrumbService]
-        };
-    }
-}
\ No newline at end of file
diff --git a/gravitee-am-ui/src/libraries/ng2-breadcrumb/components/breadcrumb.ts b/gravitee-am-ui/src/libraries/ng2-breadcrumb/components/breadcrumb.ts
deleted file mode 100644
index 954ae70edc..0000000000
--- a/gravitee-am-ui/src/libraries/ng2-breadcrumb/components/breadcrumb.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import {Component, Input, OnInit, OnChanges} from '@angular/core';
-import {Router, NavigationEnd} from '@angular/router';
-import {BreadcrumbService} from './breadcrumbService';
-
-/**
- * This component shows a breadcrumb trail for available routes the router can navigate to.
- * It subscribes to the router in order to update the breadcrumb trail as you navigate to a component.
- */
-@Component({
-    selector: 'breadcrumb',
-    template: `
-        <ul [class.breadcrumb]="useBootstrap">
-            <li *ngFor="let url of _urls; let last = last" [ngClass]="{'breadcrumb-item': useBootstrap, 'active': last}"> <!-- disable link of last item -->
-                <a role="button" *ngIf="!last && url == prefix" (click)="navigateTo('/')">{{url}}</a>
-                <a role="button" *ngIf="!last && url != prefix" (click)="navigateTo(url)">{{friendlyName(url)}}</a>
-                <span *ngIf="last">{{friendlyName(url)}}</span>
-                <span *ngIf="last && url == prefix">{{friendlyName('/')}}</span>
-            </li>
-        </ul>
-    `
-})
-export class BreadcrumbComponent implements OnInit, OnChanges {
-    @Input() useBootstrap: boolean = true;
-    @Input() prefix:       string  = '';
-
-    public _urls: string[];
-    public _routerSubscription: any;
-
-    constructor(
-        private router: Router,
-        private breadcrumbService: BreadcrumbService
-    ) {
-      this._urls = new Array();
-
-      if (this.prefix.length > 0) {
-        this._urls.unshift(this.prefix);
-      }
-
-      this._routerSubscription = this.router.events.subscribe((navigationEnd:NavigationEnd) => {
-
-        if (navigationEnd instanceof NavigationEnd) {
-          this._urls.length = 0; //Fastest way to clear out array
-          this.generateBreadcrumbTrail(navigationEnd.urlAfterRedirects ? navigationEnd.urlAfterRedirects : navigationEnd.url);
-        }
-      });
-    }
-
-    ngOnInit(): void { }
-
-    ngOnChanges(changes: any): void {
-        if (!this._urls) {
-            return;
-        }
-
-        this._urls.length = 0;
-        this.generateBreadcrumbTrail(this.router.url);
-    }
-
-    generateBreadcrumbTrail(url: string): void {
-        if (!this.breadcrumbService.isRouteHidden(url)) {
-            //Add url to beginning of array (since the url is being recursively broken down from full url to its parent)
-            this._urls.unshift(url);
-        }
-
-        if (url.lastIndexOf('/') > 0) {
-            this.generateBreadcrumbTrail(url.substr(0, url.lastIndexOf('/'))); //Find last '/' and add everything before it as a parent route
-        } else if (this.prefix.length > 0) {
-            this._urls.unshift(this.prefix);
-        }
-    }
-
-    navigateTo(url: string): void {
-        this.router.navigateByUrl(url);
-    }
-
-    friendlyName(url: string): string {
-        return !url ? '' : this.breadcrumbService.getFriendlyNameForRoute(url);
-    }
-
-    ngOnDestroy(): void {
-        this._routerSubscription.unsubscribe();
-    }
-
-}
diff --git a/gravitee-am-ui/src/libraries/ng2-breadcrumb/components/breadcrumbService.ts b/gravitee-am-ui/src/libraries/ng2-breadcrumb/components/breadcrumbService.ts
deleted file mode 100644
index ed012651bd..0000000000
--- a/gravitee-am-ui/src/libraries/ng2-breadcrumb/components/breadcrumbService.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import {Injectable} from "@angular/core";
-
-@Injectable()
-export class BreadcrumbService {
-
-    private routesFriendlyNames: Map<string, string> = new Map<string, string>();
-    private routesFriendlyNamesRegex: Map<string, string> = new Map<string, string>();
-    private routesWithCallback: Map<string, (string:string) => string> = new Map<string, (string:string) => string>();
-    private routesWithCallbackRegex: Map<string, (string:string) => string> = new Map<string, (string:string) => string>();
-    private hideRoutes: any = new Array<string>();
-    private hideRoutesRegex: any = new Array<string>();
-
-    /**
-     * Specify a friendly name for the corresponding route.
-     *
-     * @param route
-     * @param name
-     */
-    addFriendlyNameForRoute(route: string, name: string): void {
-        this.routesFriendlyNames.set(route, name);
-    }
-    
-    /**
-     * Specify a friendly name for the corresponding route matching a regular expression.
-     *
-     * @param route
-     * @param name
-     */
-    addFriendlyNameForRouteRegex(routeRegex: string, name: string): void {
-        this.routesFriendlyNamesRegex.set(routeRegex, name);
-    }
-    
-    /**
-     * Specify a callback for the corresponding route.
-     * When a mathing url is navigatedd to, the callback function is invoked to get the name to be displayed in the breadcrumb.
-     */
-    addCallbackForRoute(route: string, callback: (id: string) => string): void {
-        this.routesWithCallback.set(route, callback);
-    }
-    
-    /**
-     * Specify a callback for the corresponding route matching a regular expression.
-     * When a mathing url is navigatedd to, the callback function is invoked to get the name to be displayed in the breadcrumb.
-     */
-    addCallbackForRouteRegex(routeRegex: string, callback: (id: string) => string): void {
-        this.routesWithCallbackRegex.set(routeRegex, callback);
-    }
-
-    /**
-     * Show the friendly name for a given route (url). If no match is found the url (without the leading '/') is shown.
-     *
-     * @param route
-     * @returns {*}
-     */
-    getFriendlyNameForRoute(route: string): string {
-        let name: string;
-        let routeEnd = route.substr(route.lastIndexOf('/')+1, route.length);
-        
-        this.routesFriendlyNames.forEach((value, key, map) => {
-            if (key === route) {
-                name = value;
-            }
-        });
-        
-        this.routesFriendlyNamesRegex.forEach((value, key, map) => {
-            if (new RegExp(key).exec(route)) {
-                name = value;
-            }
-        });
-        
-        this.routesWithCallback.forEach((value, key, map) => {
-            if (key === route) {
-                name = value(routeEnd);
-            }
-        });
-        
-        this.routesWithCallbackRegex.forEach((value, key, map) => {
-            if (new RegExp(key).exec(route)) {
-                name = value(routeEnd);
-            }
-        });
-
-        return name ? name : routeEnd;
-    }
-    
-    /**
-     * Specify a route (url) that should not be shown in the breadcrumb.
-     */
-    hideRoute(route: string): void {
-        if (this.hideRoutes.indexOf(route) === -1) {
-            this.hideRoutes.push(route);
-        }
-    }
-    
-    /**
-     * Specify a route (url) regular expression that should not be shown in the breadcrumb.
-     */
-    hideRouteRegex(routeRegex: string): void {
-        if (this.hideRoutesRegex.indexOf(routeRegex) === -1) {
-            this.hideRoutesRegex.push(routeRegex);
-        }
-    }
-    
-    /**
-     * Returns true if a route should be hidden.
-     */
-    isRouteHidden(route: string): boolean {
-        let hide = this.hideRoutes.indexOf(route) > -1;
-        
-        this.hideRoutesRegex.forEach((value:any) => {
-            if (new RegExp(value).exec(route)) {
-                hide = true;
-            }
-        });
-        
-        return hide;
-    }
-}
diff --git a/gravitee-am-ui/src/styles.scss b/gravitee-am-ui/src/styles.scss
index b8a73d100a..3860d9747c 100644
--- a/gravitee-am-ui/src/styles.scss
+++ b/gravitee-am-ui/src/styles.scss
@@ -262,3 +262,8 @@ p.gv-plugin-creation-description {
 .no-padding-dialog-container .mat-dialog-container {
   padding: 0;
 }
+
+.mat-chip.mat-default-chip {
+  background-color: #039be5;
+  color: #fafafa;
+}


Test patch:
diff --git a/gravitee-am-management-api/gravitee-am-management-api-rest/src/test/java/io/gravitee/am/management/handlers/management/api/authentication/service/AuthenticationServiceTest.java b/gravitee-am-management-api/gravitee-am-management-api-rest/src/test/java/io/gravitee/am/management/handlers/management/api/authentication/service/AuthenticationServiceTest.java
index d2e199b1bd..ecc6a6a041 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-rest/src/test/java/io/gravitee/am/management/handlers/management/api/authentication/service/AuthenticationServiceTest.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-rest/src/test/java/io/gravitee/am/management/handlers/management/api/authentication/service/AuthenticationServiceTest.java
@@ -18,6 +18,7 @@ package io.gravitee.am.management.handlers.management.api.authentication.service
 import io.gravitee.am.identityprovider.api.DefaultUser;
 import io.gravitee.am.management.handlers.management.api.authentication.service.impl.AuthenticationServiceImpl;
 import io.gravitee.am.model.*;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.service.AuditService;
 import io.gravitee.am.service.MembershipService;
@@ -84,7 +85,7 @@ public class AuthenticationServiceTest {
         when(userServiceMock.findByUsernameAndSource(ReferenceType.ORGANIZATION, ORGANIZATION_ID, userDetailsMock.getUsername(), null)).thenReturn(Maybe.empty());
         when(userServiceMock.create(any(io.gravitee.am.model.User.class))).thenReturn(Single.just(user));
         when(userServiceMock.enhance(any())).thenReturn(Single.just(user));
-        when(roleServiceMock.findSystemRole(SystemRole.ORGANIZATION_USER, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(new Role()));
+        when(roleServiceMock.findDefaultRole(ORGANIZATION_ID, DefaultRole.ORGANIZATION_USER, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(new Role()));
         when(membershipServiceMock.addOrUpdate(eq(ORGANIZATION_ID), any(Membership.class))).thenReturn(Single.just(new Membership()));
         authenticationService.onAuthenticationSuccess(authenticationMock);
 
diff --git a/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgraderTest.java b/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgraderTest.java
index 88693f5907..d52eeabe1b 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgraderTest.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultOrganizationUpgraderTest.java
@@ -15,7 +15,6 @@
  */
 package io.gravitee.am.management.service.impl.upgrades;
 
-import io.gravitee.am.management.service.impl.upgrades.DefaultOrganizationUpgrader;
 import io.gravitee.am.management.service.impl.upgrades.helpers.MembershipHelper;
 import io.gravitee.am.model.*;
 import io.gravitee.am.repository.exceptions.TechnicalException;
@@ -85,7 +84,7 @@ public class DefaultOrganizationUpgraderTest {
                 && user.getSource().equals(idp.getId())
                 && user.getReferenceType() == ReferenceType.ORGANIZATION
                 && user.getReferenceId().equals(Organization.DEFAULT)))).thenReturn(Single.just(adminUser));
-        doNothing().when(membershipHelper).setOrganizationAdminRole(argThat(user -> user.getId().equals(adminUser.getId())));
+        doNothing().when(membershipHelper).setOrganizationPrimaryOwnerRole(argThat(user -> user.getId().equals(adminUser.getId())));
 
         assertTrue(cut.upgrade());
     }
@@ -174,7 +173,7 @@ public class DefaultOrganizationUpgraderTest {
         when(identityProviderService.findAll(ReferenceType.ORGANIZATION, Organization.DEFAULT)).thenReturn(Single.just(Collections.singletonList(idp)));
         when(userService.findByUsernameAndSource(ReferenceType.ORGANIZATION, Organization.DEFAULT, "admin", idp.getId())).thenReturn(Maybe.empty());
         when(userService.create(any(User.class))).thenReturn(Single.just(adminUser));
-        doNothing().when(membershipHelper).setOrganizationAdminRole(argThat(user -> user.getId().equals(adminUser.getId())));
+        doNothing().when(membershipHelper).setOrganizationPrimaryOwnerRole(argThat(user -> user.getId().equals(adminUser.getId())));
 
         when(organizationService.createDefault()).thenReturn(Maybe.empty());
         assertTrue(cut.upgrade());
diff --git a/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgraderTest.java b/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgraderTest.java
index 0e97a66226..841a681e57 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgraderTest.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/DefaultRoleUpgraderTest.java
@@ -21,7 +21,7 @@ import io.gravitee.am.model.ReferenceType;
 import io.gravitee.am.model.Role;
 import io.gravitee.am.model.User;
 import io.gravitee.am.model.common.Page;
-import io.gravitee.am.model.permissions.SystemRole;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.repository.exceptions.TechnicalException;
 import io.gravitee.am.service.RoleService;
 import io.gravitee.am.service.UserService;
@@ -73,13 +73,13 @@ public class DefaultRoleUpgraderTest {
     public void shouldCreateSystemRoles() {
 
         when(roleService.createOrUpdateSystemRoles()).thenReturn(Completable.complete());
-        when(roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(new Role()));
+        when(roleService.findDefaultRole(Organization.DEFAULT, DefaultRole.ORGANIZATION_OWNER, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(new Role()));
 
         cut.upgrade();
     }
 
     @Test
-    public void shouldCreateSystemRoles_setAdminRoleToExistingUsers() {
+    public void shouldCreateSystemRoles_setOwnerRoleToExistingUsers() {
 
         int totalUsers = 22;
 
@@ -94,7 +94,7 @@ public class DefaultRoleUpgraderTest {
 
         when(roleService.createOrUpdateSystemRoles()).thenReturn(Completable.complete());
 
-        when(roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION))
+        when(roleService.findDefaultRole(Organization.DEFAULT, DefaultRole.ORGANIZATION_OWNER, ReferenceType.ORGANIZATION))
                 .thenReturn(Maybe.empty()) // Role does not exist at start.
                 .thenReturn(Maybe.just(adminRole)); // Role has been created.
 
@@ -118,7 +118,7 @@ public class DefaultRoleUpgraderTest {
     public void shouldCreateSystemRoles_technicalError() {
 
         when(roleService.createOrUpdateSystemRoles()).thenReturn(Completable.error(TechnicalException::new));
-        when(roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION)).thenReturn(Maybe.empty());
+        when(roleService.findDefaultRole(Organization.DEFAULT, DefaultRole.ORGANIZATION_OWNER, ReferenceType.ORGANIZATION)).thenReturn(Maybe.empty());
 
         assertFalse(cut.upgrade());
     }
diff --git a/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelperTest.java b/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelperTest.java
index 685a417c60..08a54f40dd 100644
--- a/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelperTest.java
+++ b/gravitee-am-management-api/gravitee-am-management-api-service/src/test/java/io/gravitee/am/management/service/impl/upgrades/helpers/MembershipHelperTest.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.management.service.impl.upgrades.helpers;
 
 import io.gravitee.am.model.*;
+import io.gravitee.am.model.permissions.DefaultRole;
 import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.repository.management.api.search.MembershipCriteria;
 import io.gravitee.am.service.MembershipService;
@@ -29,8 +30,6 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
-import java.util.Arrays;
-
 import static org.mockito.Mockito.*;
 
 /**
@@ -55,23 +54,23 @@ public class MembershipHelperTest {
     }
 
     @Test
-    public void shouldSetOrganizationAdminRole() {
+    public void shouldSetOrganizationPrimaryOwnerRole() {
 
         User user = new User();
         user.setId("user-id");
 
-        final Role adminRole = new Role();
-        adminRole.setId("role-id");
+        final Role primaryOwnerRole = new Role();
+        primaryOwnerRole.setId("role-id");
 
         when(membershipService.findByCriteria(eq(ReferenceType.ORGANIZATION), eq(Organization.DEFAULT), any(MembershipCriteria.class))).thenReturn(Flowable.empty()); // user has no role yet.
-        when(roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(adminRole));
+        when(roleService.findSystemRole(SystemRole.ORGANIZATION_PRIMARY_OWNER, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(primaryOwnerRole));
         when(membershipService.addOrUpdate(eq(Organization.DEFAULT), any(Membership.class))).thenReturn(Single.just(new Membership()));
 
-        cut.setOrganizationAdminRole(user);
+        cut.setOrganizationPrimaryOwnerRole(user);
     }
 
     @Test
-    public void shouldNotSetOrganizationAdminRole_alreadyHasARole() {
+    public void shouldNotSetOrganizationPrimaryOwnerRole_alreadyHasARole() {
 
         User user = new User();
         user.setId("user-id");
@@ -80,8 +79,8 @@ public class MembershipHelperTest {
         adminRole.setId("role-id");
 
         when(membershipService.findByCriteria(eq(ReferenceType.ORGANIZATION), eq(Organization.DEFAULT), any(MembershipCriteria.class))).thenReturn(Flowable.just(new Membership()));
-        when(roleService.findSystemRole(SystemRole.ORGANIZATION_ADMIN, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(adminRole));
+        when(roleService.findSystemRole(SystemRole.ORGANIZATION_PRIMARY_OWNER, ReferenceType.ORGANIZATION)).thenReturn(Maybe.just(adminRole));
 
-        cut.setOrganizationAdminRole(user);
+        cut.setOrganizationPrimaryOwnerRole(user);
     }
 }
\ No newline at end of file
diff --git a/gravitee-am-service/src/test/java/io/gravitee/am/service/MembershipServiceTest.java b/gravitee-am-service/src/test/java/io/gravitee/am/service/MembershipServiceTest.java
index e32c7bdb66..715215edf4 100644
--- a/gravitee-am-service/src/test/java/io/gravitee/am/service/MembershipServiceTest.java
+++ b/gravitee-am-service/src/test/java/io/gravitee/am/service/MembershipServiceTest.java
@@ -18,7 +18,7 @@ package io.gravitee.am.service;
 import io.gravitee.am.model.*;
 import io.gravitee.am.model.common.event.Event;
 import io.gravitee.am.model.membership.MemberType;
-import io.gravitee.am.repository.exceptions.TechnicalException;
+import io.gravitee.am.model.permissions.SystemRole;
 import io.gravitee.am.repository.management.api.MembershipRepository;
 import io.gravitee.am.service.exception.*;
 import io.gravitee.am.service.impl.MembershipServiceImpl;
@@ -26,7 +26,6 @@ import io.reactivex.Flowable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import io.reactivex.observers.TestObserver;
-import io.reactivex.subscribers.TestSubscriber;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
@@ -44,7 +43,7 @@ import static org.mockito.Mockito.*;
 public class MembershipServiceTest {
 
     public static final String ORGANIZATION_ID = "orga#1";
-    public static final String MASTER_DOMAIN_ID = "master-domain";
+    public static final String DOMAIN_ID = "master-domain";
 
     @InjectMocks
     private MembershipService membershipService = new MembershipServiceImpl();
@@ -71,7 +70,7 @@ public class MembershipServiceTest {
     public void shouldCreate_userMembership() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("user-id");
         membership.setMemberType(MemberType.USER);
@@ -83,7 +82,7 @@ public class MembershipServiceTest {
 
         Role role = new Role();
         role.setId("role-id");
-        role.setReferenceId(MASTER_DOMAIN_ID);
+        role.setReferenceId(DOMAIN_ID);
         role.setReferenceType(ReferenceType.DOMAIN);
         role.setAssignableType(ReferenceType.DOMAIN);
 
@@ -100,11 +99,47 @@ public class MembershipServiceTest {
         testObserver.assertNoErrors();
     }
 
+    @Test
+    public void shouldCreate_primaryOwner() {
+
+        Membership membership = new Membership();
+        membership.setReferenceId(DOMAIN_ID);
+        membership.setReferenceType(ReferenceType.DOMAIN);
+        membership.setMemberId("user-id");
+        membership.setMemberType(MemberType.USER);
+        membership.setRoleId("role-primary-owner");
+
+        User user = new User();
+        user.setReferenceId(ORGANIZATION_ID);
+        user.setReferenceType(ReferenceType.ORGANIZATION);
+
+        Role role = new Role();
+        role.setId("role-primary-owner");
+        role.setName(SystemRole.DOMAIN_PRIMARY_OWNER.name());
+        role.setReferenceId(Platform.DEFAULT);
+        role.setSystem(true);
+        role.setReferenceType(ReferenceType.PLATFORM);
+        role.setAssignableType(ReferenceType.DOMAIN);
+
+        when(userService.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, membership.getMemberId())).thenReturn(Single.just(user));
+        when(roleService.findById(role.getId())).thenReturn(Maybe.just(role));
+        when(membershipRepository.findByReferenceAndMember(membership.getReferenceType(), membership.getReferenceId(), membership.getMemberType(), membership.getMemberId())).thenReturn(Maybe.empty());
+        when(membershipRepository.create(any())).thenReturn(Single.just(new Membership()));
+        when(membershipRepository.findByCriteria(eq(ReferenceType.DOMAIN), eq(DOMAIN_ID), argThat(criteria -> criteria.getRoleId().isPresent()))).thenReturn(Flowable.empty());
+        when(eventService.create(any())).thenReturn(Single.just(new Event()));
+
+        TestObserver testObserver = membershipService.addOrUpdate(ORGANIZATION_ID, membership).test();
+        testObserver.awaitTerminalEvent();
+
+        testObserver.assertComplete();
+        testObserver.assertNoErrors();
+    }
+
     @Test
     public void shouldCreate_groupMembership() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("group-id");
         membership.setMemberType(MemberType.GROUP);
@@ -112,12 +147,12 @@ public class MembershipServiceTest {
 
         Role role = new Role();
         role.setId("role-id");
-        role.setReferenceId(MASTER_DOMAIN_ID);
+        role.setReferenceId(DOMAIN_ID);
         role.setReferenceType(ReferenceType.DOMAIN);
         role.setAssignableType(ReferenceType.DOMAIN);
 
         Group group = new Group();
-        group.setReferenceId(MASTER_DOMAIN_ID);
+        group.setReferenceId(DOMAIN_ID);
         group.setReferenceType(ReferenceType.DOMAIN);
 
         when(groupService.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, membership.getMemberId())).thenReturn(Single.just(group));
@@ -137,7 +172,7 @@ public class MembershipServiceTest {
     public void shouldNotCreate_memberNotFound() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("user-id");
         membership.setMemberType(MemberType.USER);
@@ -145,7 +180,7 @@ public class MembershipServiceTest {
 
         Role role = new Role();
         role.setId("role-id");
-        role.setReferenceId(MASTER_DOMAIN_ID);
+        role.setReferenceId(DOMAIN_ID);
         role.setReferenceType(ReferenceType.DOMAIN);
         role.setAssignableType(ReferenceType.DOMAIN);
 
@@ -162,11 +197,74 @@ public class MembershipServiceTest {
         verify(membershipRepository, never()).create(any());
     }
 
+    @Test
+    public void shouldNotCreate_primaryOwnerAlreadyAssigned() {
+
+        Membership membership = new Membership();
+        membership.setReferenceId(DOMAIN_ID);
+        membership.setReferenceType(ReferenceType.DOMAIN);
+        membership.setMemberId("user-id");
+        membership.setMemberType(MemberType.USER);
+        membership.setRoleId("role-primary-owner");
+
+        User user = new User();
+        user.setReferenceId(ORGANIZATION_ID);
+        user.setReferenceType(ReferenceType.ORGANIZATION);
+
+        Role role = new Role();
+        role.setId("role-primary-owner");
+        role.setName(SystemRole.DOMAIN_PRIMARY_OWNER.name());
+        role.setReferenceId(Platform.DEFAULT);
+        role.setSystem(true);
+        role.setReferenceType(ReferenceType.PLATFORM);
+        role.setAssignableType(ReferenceType.DOMAIN);
+
+        when(userService.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, membership.getMemberId())).thenReturn(Single.just(user));
+        when(roleService.findById(role.getId())).thenReturn(Maybe.just(role));
+        when(membershipRepository.findByCriteria(eq(ReferenceType.DOMAIN), eq(DOMAIN_ID), argThat(criteria -> criteria.getRoleId().isPresent()))).thenReturn(Flowable.just(new Membership()));
+        when(membershipRepository.findByReferenceAndMember(membership.getReferenceType(), membership.getReferenceId(), membership.getMemberType(), membership.getMemberId())).thenReturn(Maybe.empty());
+
+        TestObserver testObserver = membershipService.addOrUpdate(ORGANIZATION_ID, membership).test();
+        testObserver.awaitTerminalEvent();
+        testObserver.assertError(SinglePrimaryOwnerException.class);
+    }
+
+    @Test
+    public void shouldNotCreate_primaryOwnerCantBeAssignedToGroup() {
+
+        Membership membership = new Membership();
+        membership.setReferenceId(DOMAIN_ID);
+        membership.setReferenceType(ReferenceType.DOMAIN);
+        membership.setMemberId("group-id");
+        membership.setMemberType(MemberType.GROUP);
+        membership.setRoleId("role-primary-owner");
+
+        Group group = new Group();
+        group.setReferenceId(ORGANIZATION_ID);
+        group.setReferenceType(ReferenceType.ORGANIZATION);
+
+        Role role = new Role();
+        role.setId("role-primary-owner");
+        role.setName(SystemRole.DOMAIN_PRIMARY_OWNER.name());
+        role.setReferenceId(Platform.DEFAULT);
+        role.setSystem(true);
+        role.setReferenceType(ReferenceType.PLATFORM);
+        role.setAssignableType(ReferenceType.DOMAIN);
+
+        when(groupService.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, membership.getMemberId())).thenReturn(Single.just(group));
+        when(roleService.findById(role.getId())).thenReturn(Maybe.just(role));
+        when(membershipRepository.findByReferenceAndMember(membership.getReferenceType(), membership.getReferenceId(), membership.getMemberType(), membership.getMemberId())).thenReturn(Maybe.empty());
+
+        TestObserver testObserver = membershipService.addOrUpdate(ORGANIZATION_ID, membership).test();
+        testObserver.awaitTerminalEvent();
+        testObserver.assertError(InvalidRoleException.class);
+    }
+
     @Test
     public void shouldNotCreate_groupNotFound() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("group-id");
         membership.setMemberType(MemberType.GROUP);
@@ -174,7 +272,7 @@ public class MembershipServiceTest {
 
         Role role = new Role();
         role.setId("role-id");
-        role.setReferenceId(MASTER_DOMAIN_ID);
+        role.setReferenceId(DOMAIN_ID);
         role.setReferenceType(ReferenceType.DOMAIN);
         role.setAssignableType(ReferenceType.DOMAIN);
 
@@ -195,7 +293,7 @@ public class MembershipServiceTest {
     public void shouldNotCreate_roleNotFound() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("user-id");
         membership.setMemberType(MemberType.USER);
@@ -228,7 +326,7 @@ public class MembershipServiceTest {
     public void shouldNotCreate_invalidRoleScope() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("user-id");
         membership.setMemberType(MemberType.USER);
@@ -240,7 +338,7 @@ public class MembershipServiceTest {
 
         Role role = new Role();
         role.setId("role-id");
-        role.setReferenceId(MASTER_DOMAIN_ID);
+        role.setReferenceId(DOMAIN_ID);
         role.setReferenceType(ReferenceType.DOMAIN);
         // Scope application can't be use for domain.
         role.setAssignableType(ReferenceType.APPLICATION);
@@ -262,7 +360,7 @@ public class MembershipServiceTest {
     public void shouldNotCreate_invalidDomain() {
 
         Membership membership = new Membership();
-        membership.setReferenceId(MASTER_DOMAIN_ID);
+        membership.setReferenceId(DOMAIN_ID);
         membership.setReferenceType(ReferenceType.DOMAIN);
         membership.setMemberId("user-id");
         membership.setMemberType(MemberType.USER);
@@ -325,4 +423,6 @@ public class MembershipServiceTest {
 
         verify(membershipRepository, never()).create(any());
     }
+
+
 }
diff --git a/gravitee-am-service/src/test/java/io/gravitee/am/service/OrganizationServiceTest.java b/gravitee-am-service/src/test/java/io/gravitee/am/service/OrganizationServiceTest.java
index 3274ad1855..e726ebc50d 100644
--- a/gravitee-am-service/src/test/java/io/gravitee/am/service/OrganizationServiceTest.java
+++ b/gravitee-am-service/src/test/java/io/gravitee/am/service/OrganizationServiceTest.java
@@ -29,6 +29,7 @@ import io.gravitee.am.service.exception.TechnicalManagementException;
 import io.gravitee.am.service.impl.OrganizationServiceImpl;
 import io.gravitee.am.service.model.NewOrganization;
 import io.gravitee.am.service.model.PatchOrganization;
+import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import io.reactivex.observers.TestObserver;
@@ -40,7 +41,6 @@ import org.mockito.runners.MockitoJUnitRunner;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.argThat;
@@ -59,6 +59,9 @@ public class OrganizationServiceTest {
     @Mock
     private OrganizationRepository organizationRepository;
 
+    @Mock
+    private RoleService roleService;
+
     @Mock
     private AuditService auditService;
 
@@ -67,7 +70,7 @@ public class OrganizationServiceTest {
     @Before
     public void before() {
 
-        cut = new OrganizationServiceImpl(organizationRepository, auditService);
+        cut = new OrganizationServiceImpl(organizationRepository, roleService, auditService);
     }
 
     @Test
@@ -113,6 +116,7 @@ public class OrganizationServiceTest {
 
         when(organizationRepository.count()).thenReturn(Single.just(0L));
         when(organizationRepository.create(argThat(organization -> organization.getId().equals(Organization.DEFAULT)))).thenReturn(Single.just(defaultOrganization));
+        when(roleService.createDefaultRoles("DEFAULT")).thenReturn(Completable.complete());
 
         TestObserver<Organization> obs = cut.createDefault().test();
 
@@ -178,6 +182,7 @@ public class OrganizationServiceTest {
 
         when(organizationRepository.findById(ORGANIZATION_ID)).thenReturn(Maybe.empty());
         when(organizationRepository.create(argThat(organization -> organization.getId().equals(ORGANIZATION_ID)))).thenAnswer(i -> Single.just(i.getArgument(0)));
+        when(roleService.createDefaultRoles(ORGANIZATION_ID)).thenReturn(Completable.complete());
 
         NewOrganization newOrganization = new NewOrganization();
         newOrganization.setName("TestName");
diff --git a/gravitee-am-service/src/test/java/io/gravitee/am/service/RoleServiceTest.java b/gravitee-am-service/src/test/java/io/gravitee/am/service/RoleServiceTest.java
index cbc040008c..6c71478a62 100644
--- a/gravitee-am-service/src/test/java/io/gravitee/am/service/RoleServiceTest.java
+++ b/gravitee-am-service/src/test/java/io/gravitee/am/service/RoleServiceTest.java
@@ -15,10 +15,13 @@
  */
 package io.gravitee.am.service;
 
+import io.gravitee.am.model.Acl;
 import io.gravitee.am.model.Platform;
 import io.gravitee.am.model.ReferenceType;
 import io.gravitee.am.model.Role;
 import io.gravitee.am.model.common.event.Event;
+import io.gravitee.am.model.permissions.DefaultRole;
+import io.gravitee.am.model.permissions.Permission;
 import io.gravitee.am.repository.exceptions.TechnicalException;
 import io.gravitee.am.repository.management.api.RoleRepository;
 import io.gravitee.am.service.exception.*;
@@ -53,6 +56,7 @@ import static org.mockito.Mockito.*;
 @RunWith(MockitoJUnitRunner.class)
 public class RoleServiceTest {
 
+    public static final String ORGANIZATION_ID = "orga#1";
     @InjectMocks
     private RoleService roleService = new RoleServiceImpl();
 
@@ -218,6 +222,34 @@ public class RoleServiceTest {
         verify(roleRepository, times(1)).update(any(Role.class));
     }
 
+    @Test
+    public void shouldUpdate_defaultRolePermissions() {
+        UpdateRole updateRole = new UpdateRole();
+        updateRole.setName(DefaultRole.DOMAIN_USER.name());
+        updateRole.setPermissions(Permission.flatten(Collections.singletonMap(Permission.DOMAIN, Collections.singleton(Acl.READ))));
+
+        Role role = new Role();
+        role.setName(DefaultRole.DOMAIN_USER.name());
+        role.setDefaultRole(true); // should be able to update a default role.
+        role.setReferenceType(ReferenceType.ORGANIZATION);
+        role.setReferenceId(ORGANIZATION_ID);
+
+        when(roleRepository.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role")).thenReturn(Maybe.just(role));
+        when(roleRepository.findAll(ReferenceType.ORGANIZATION, ORGANIZATION_ID)).thenReturn(Flowable.empty());
+        when(roleRepository.update(argThat(r -> r.getPermissionAcls().equals(Permission.unflatten(updateRole.getPermissions()))))).thenReturn(Single.just(role));
+        when(eventService.create(any())).thenReturn(Single.just(new Event()));
+
+        TestObserver testObserver = roleService.update(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role", updateRole, null).test();
+        testObserver.awaitTerminalEvent();
+
+        testObserver.assertComplete();
+        testObserver.assertNoErrors();
+
+        verify(roleRepository, times(1)).findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role");
+        verify(roleRepository, times(1)).findAll(ReferenceType.ORGANIZATION, ORGANIZATION_ID);
+        verify(roleRepository, times(1)).update(any(Role.class));
+    }
+
     @Test
     public void shouldUpdate_technicalException() {
         UpdateRole updateRole = Mockito.mock(UpdateRole.class);
@@ -273,19 +305,49 @@ public class RoleServiceTest {
 
     @Test
     public void shouldNotUpdate_systemRole() {
-        UpdateRole updateRole = Mockito.mock(UpdateRole.class);
-        Role role = Mockito.mock(Role.class);
-        when(role.isSystem()).thenReturn(true);
-        when(roleRepository.findById(ReferenceType.DOMAIN, DOMAIN, "my-role")).thenReturn(Maybe.just(role));
+        UpdateRole updateRole = new UpdateRole();
 
-        TestObserver testObserver = roleService.update(DOMAIN, "my-role", updateRole).test();
+        Role role = new Role();
+        role.setSystem(true);
+        role.setReferenceType(ReferenceType.ORGANIZATION);
+        role.setReferenceId(ORGANIZATION_ID);
+
+        when(roleRepository.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role")).thenReturn(Maybe.just(role));
+
+        TestObserver testObserver = roleService.update(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role", updateRole, null).test();
         testObserver.awaitTerminalEvent();
 
         testObserver.assertNotComplete();
         testObserver.assertError(SystemRoleUpdateException.class);
 
-        verify(roleRepository, times(1)).findById(ReferenceType.DOMAIN, DOMAIN, "my-role");
-        verify(roleRepository, never()).findAll(ReferenceType.DOMAIN, DOMAIN);
+        verify(roleRepository, times(1)).findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role");
+        verify(roleRepository, never()).findAll(ReferenceType.ORGANIZATION, ORGANIZATION_ID);
+        verify(roleRepository, never()).update(any(Role.class));
+    }
+
+    @Test
+    public void shouldNotUpdate_defaultRoleName() {
+
+        UpdateRole updateRole = new UpdateRole();
+        updateRole.setName("new name");
+
+        Role role = new Role();
+        role.setId("my-role");
+        role.setName(DefaultRole.DOMAIN_USER.name());
+        role.setDefaultRole(true);
+        role.setReferenceType(ReferenceType.ORGANIZATION);
+        role.setReferenceId(ORGANIZATION_ID);
+
+        when(roleRepository.findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role")).thenReturn(Maybe.just(role));
+
+        TestObserver testObserver = roleService.update(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role", updateRole, null).test();
+        testObserver.awaitTerminalEvent();
+
+        testObserver.assertNotComplete();
+        testObserver.assertError(DefaultRoleUpdateException.class);
+
+        verify(roleRepository, times(1)).findById(ReferenceType.ORGANIZATION, ORGANIZATION_ID, "my-role");
+        verify(roleRepository, never()).findAll(ReferenceType.ORGANIZATION, ORGANIZATION_ID);
         verify(roleRepository, never()).update(any(Role.class));
     }
 
@@ -303,6 +365,8 @@ public class RoleServiceTest {
 
     @Test
     public void shouldDelete_technicalException() {
+
+        when(eventService.create(any(Event.class))).thenReturn(Single.just(new Event()));
         when(roleRepository.findById(eq(ReferenceType.DOMAIN), eq(DOMAIN), eq("my-role"))).thenReturn(Maybe.just(new Role()));
         when(roleRepository.delete(anyString())).thenReturn(Completable.error(TechnicalException::new));