diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupEntity.java index 8089e1ad30..2bd9cac1ae 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupEntity.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupEntity.java @@ -33,19 +33,22 @@ public class GroupEntity { private String name; @JsonProperty("event_rules") private List eventRules; - boolean manageable; - - /** - * Roles - */ private Map roles; - @JsonProperty("created_at") private Date createdAt; - @JsonProperty("updated_at") private Date updatedAt; + @JsonProperty("max_invitation") + private Integer maxInvitation; + @JsonProperty("lock_api_role") + private boolean lockApiRole; + @JsonProperty("lock_application_role") + private boolean lockApplicationRole; + @JsonProperty("system_invitation") + private boolean systemInvitation; + @JsonProperty("email_invitation") + private boolean emailInvitation; public String getId() { return id; @@ -103,6 +106,46 @@ public void setRoles(Map roles) { this.roles = roles; } + public Integer getMaxInvitation() { + return maxInvitation; + } + + public void setMaxInvitation(Integer maxInvitation) { + this.maxInvitation = maxInvitation; + } + + public boolean isLockApiRole() { + return lockApiRole; + } + + public void setLockApiRole(boolean lockApiRole) { + this.lockApiRole = lockApiRole; + } + + public boolean isLockApplicationRole() { + return lockApplicationRole; + } + + public void setLockApplicationRole(boolean lockApplicationRole) { + this.lockApplicationRole = lockApplicationRole; + } + + public boolean isSystemInvitation() { + return systemInvitation; + } + + public void setSystemInvitation(boolean systemInvitation) { + this.systemInvitation = systemInvitation; + } + + public boolean isEmailInvitation() { + return emailInvitation; + } + + public void setEmailInvitation(boolean emailInvitation) { + this.emailInvitation = emailInvitation; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -121,9 +164,16 @@ public String toString() { return "GroupEntity{" + "id='" + id + '\'' + ", name='" + name + '\'' + - ", eventRules='" + eventRules + '\'' + + ", eventRules=" + eventRules + + ", manageable=" + manageable + + ", roles=" + roles + ", createdAt=" + createdAt + ", updatedAt=" + updatedAt + - "}"; + ", maxInvitation=" + maxInvitation + + ", lockApiRole=" + lockApiRole + + ", lockApplicationRole=" + lockApplicationRole + + ", systemInvitation=" + systemInvitation + + ", emailInvitation=" + emailInvitation + + '}'; } } diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupMemberEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupMemberEntity.java index d503205ded..f4c825d19e 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupMemberEntity.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/GroupMemberEntity.java @@ -36,14 +36,10 @@ public GroupMemberEntity(MemberEntity memberEntity) { } private String id; - private String displayName; - private Map roles; - @JsonProperty("created_at") private Date createdAt; - @JsonProperty("updated_at") private Date updatedAt; diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/InvitationEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/InvitationEntity.java new file mode 100644 index 0000000000..8b64ea8cb5 --- /dev/null +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/InvitationEntity.java @@ -0,0 +1,133 @@ +/** + * 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.management.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; +import java.util.Objects; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public class InvitationEntity { + private String id; + @JsonProperty("reference_type") + private InvitationReferenceType referenceType; + @JsonProperty("reference_id") + private String referenceId; + private String email; + @JsonProperty("api_role") + private String apiRole; + @JsonProperty("application_role") + private String applicationRole; + @JsonProperty("created_at") + private Date createdAt; + @JsonProperty("updated_at") + private Date updatedAt; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public InvitationReferenceType getReferenceType() { + return referenceType; + } + + public void setReferenceType(InvitationReferenceType referenceType) { + this.referenceType = referenceType; + } + + public String getReferenceId() { + return referenceId; + } + + public void setReferenceId(String referenceId) { + this.referenceId = referenceId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getApiRole() { + return apiRole; + } + + public void setApiRole(String apiRole) { + this.apiRole = apiRole; + } + + public String getApplicationRole() { + return applicationRole; + } + + public void setApplicationRole(String applicationRole) { + this.applicationRole = applicationRole; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + public Date getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof InvitationEntity)) return false; + InvitationEntity that = (InvitationEntity) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "InvitationEntity{" + + "id='" + id + '\'' + + ", referenceType=" + referenceType + + ", referenceId='" + referenceId + '\'' + + ", email='" + email + '\'' + + ", apiRole='" + apiRole + '\'' + + ", applicationRole='" + applicationRole + '\'' + + ", createdAt=" + createdAt + + ", updatedAt=" + updatedAt + + '}'; + } +} diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/InvitationReferenceType.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/InvitationReferenceType.java new file mode 100644 index 0000000000..2f7cbc5bb5 --- /dev/null +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/InvitationReferenceType.java @@ -0,0 +1,24 @@ +/** + * 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.management.model; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public enum InvitationReferenceType { + API, APPLICATION, GROUP +} \ No newline at end of file diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewGroupEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewGroupEntity.java index ed332ca811..5b480a6c9c 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewGroupEntity.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewGroupEntity.java @@ -28,9 +28,16 @@ public class NewGroupEntity { @NotNull private String name; - @JsonProperty("event_rules") private List eventRules; + private Integer maxInvitation; + @JsonProperty("lock_api_role") + private boolean lockApiRole; + @JsonProperty("lock_application_role") + private boolean lockApplicationRole; + private boolean systemInvitation; + @JsonProperty("email_invitation") + private boolean emailInvitation; public String getName() { return name; @@ -48,11 +55,56 @@ public void setEventRules(List eventRules) { this.eventRules = eventRules; } + public Integer getMaxInvitation() { + return maxInvitation; + } + + public void setMaxInvitation(Integer maxInvitation) { + this.maxInvitation = maxInvitation; + } + + public boolean isLockApiRole() { + return lockApiRole; + } + + public void setLockApiRole(boolean lockApiRole) { + this.lockApiRole = lockApiRole; + } + + public boolean isLockApplicationRole() { + return lockApplicationRole; + } + + public void setLockApplicationRole(boolean lockApplicationRole) { + this.lockApplicationRole = lockApplicationRole; + } + + public boolean isSystemInvitation() { + return systemInvitation; + } + + public void setSystemInvitation(boolean systemInvitation) { + this.systemInvitation = systemInvitation; + } + + public boolean isEmailInvitation() { + return emailInvitation; + } + + public void setEmailInvitation(boolean emailInvitation) { + this.emailInvitation = emailInvitation; + } + @Override public String toString() { return "NewGroupEntity{" + - ", name='" + name + '\'' + - ", eventRules='" + eventRules + '\'' + - "}"; + "name='" + name + '\'' + + ", eventRules=" + eventRules + + ", maxInvitation=" + maxInvitation + + ", lockApiRole=" + lockApiRole + + ", lockApplicationRole=" + lockApplicationRole + + ", systemInvitation=" + systemInvitation + + ", emailInvitation=" + emailInvitation + + '}'; } } diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewInvitationEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewInvitationEntity.java new file mode 100644 index 0000000000..172e15dd49 --- /dev/null +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/NewInvitationEntity.java @@ -0,0 +1,107 @@ +/** + * 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.management.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; +import java.util.Objects; + +/** + * @author Azize ELAMRANI (azize at graviteesource.com) + * @author GraviteeSource Team + */ +public class NewInvitationEntity { + + @NotNull + @JsonProperty("reference_type") + private InvitationReferenceType referenceType; + @NotNull + @JsonProperty("reference_id") + private String referenceId; + @NotNull + private String email; + @JsonProperty("api_role") + private String apiRole; + @JsonProperty("application_role") + private String applicationRole; + + public InvitationReferenceType getReferenceType() { + return referenceType; + } + + public void setReferenceType(InvitationReferenceType referenceType) { + this.referenceType = referenceType; + } + + public String getReferenceId() { + return referenceId; + } + + public void setReferenceId(String referenceId) { + this.referenceId = referenceId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getApiRole() { + return apiRole; + } + + public void setApiRole(String apiRole) { + this.apiRole = apiRole; + } + + public String getApplicationRole() { + return applicationRole; + } + + public void setApplicationRole(String applicationRole) { + this.applicationRole = applicationRole; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NewInvitationEntity)) return false; + NewInvitationEntity that = (NewInvitationEntity) o; + return referenceType == that.referenceType && + Objects.equals(referenceId, that.referenceId) && + Objects.equals(email, that.email); + } + + @Override + public int hashCode() { + return Objects.hash(referenceType, referenceId, email); + } + + @Override + public String toString() { + return "NewInvitationEntity{" + + "referenceType=" + referenceType + + ", referenceId='" + referenceId + '\'' + + ", email='" + email + '\'' + + ", apiRole='" + apiRole + '\'' + + ", applicationRole='" + applicationRole + '\'' + + '}'; + } +} diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/RegisterUserEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/RegisterUserEntity.java index c1999b0ecd..f446c4164b 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/RegisterUserEntity.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/RegisterUserEntity.java @@ -25,9 +25,12 @@ public class RegisterUserEntity { @NotNull private String token; - @NotNull private String password; + @NotNull + private String firstname; + @NotNull + private String lastname; public String getToken() { return token; @@ -44,4 +47,20 @@ public String getPassword() { public void setPassword(String password) { this.password = password; } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } } diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateGroupEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateGroupEntity.java index 575cd47121..ab1d65487a 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateGroupEntity.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateGroupEntity.java @@ -30,14 +30,19 @@ public class UpdateGroupEntity { @NotNull private String name; - @JsonProperty("event_rules") private List eventRules; - - /** - * Roles - */ private Map roles; + @JsonProperty("max_invitation") + private Integer maxInvitation; + @JsonProperty("lock_api_role") + private boolean lockApiRole; + @JsonProperty("lock_application_role") + private boolean lockApplicationRole; + @JsonProperty("system_invitation") + private boolean systemInvitation; + @JsonProperty("email_invitation") + private boolean emailInvitation; public String getName() { return name; @@ -63,11 +68,57 @@ public void setRoles(Map roles) { this.roles = roles; } + public Integer getMaxInvitation() { + return maxInvitation; + } + + public void setMaxInvitation(Integer maxInvitation) { + this.maxInvitation = maxInvitation; + } + + public boolean isLockApiRole() { + return lockApiRole; + } + + public void setLockApiRole(boolean lockApiRole) { + this.lockApiRole = lockApiRole; + } + + public boolean isLockApplicationRole() { + return lockApplicationRole; + } + + public void setLockApplicationRole(boolean lockApplicationRole) { + this.lockApplicationRole = lockApplicationRole; + } + + public boolean isSystemInvitation() { + return systemInvitation; + } + + public void setSystemInvitation(boolean systemInvitation) { + this.systemInvitation = systemInvitation; + } + + public boolean isEmailInvitation() { + return emailInvitation; + } + + public void setEmailInvitation(boolean emailInvitation) { + this.emailInvitation = emailInvitation; + } + @Override public String toString() { return "UpdateGroupEntity{" + - ", name='" + name + '\'' + - ", eventRules='" + eventRules + '\'' + - "}"; + "name='" + name + '\'' + + ", eventRules=" + eventRules + + ", roles=" + roles + + ", maxInvitation=" + maxInvitation + + ", lockApiRole=" + lockApiRole + + ", lockApplicationRole=" + lockApplicationRole + + ", systemInvitation=" + systemInvitation + + ", emailInvitation=" + emailInvitation + + '}'; } } diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateInvitationEntity.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateInvitationEntity.java new file mode 100644 index 0000000000..92a043af3e --- /dev/null +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/UpdateInvitationEntity.java @@ -0,0 +1,115 @@ +/** + * 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.management.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.validation.constraints.NotNull; +import java.util.Objects; + +/** + * @author Azize ELAMRANI (azize at graviteesource.com) + * @author GraviteeSource Team + */ +public class UpdateInvitationEntity { + + private String id; + @NotNull + @JsonProperty("reference_type") + private InvitationReferenceType referenceType; + @NotNull + @JsonProperty("reference_id") + private String referenceId; + @NotNull + private String email; + @JsonProperty("api_role") + private String apiRole; + @JsonProperty("application_role") + private String applicationRole; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public InvitationReferenceType getReferenceType() { + return referenceType; + } + + public void setReferenceType(InvitationReferenceType referenceType) { + this.referenceType = referenceType; + } + + public String getReferenceId() { + return referenceId; + } + + public void setReferenceId(String referenceId) { + this.referenceId = referenceId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getApiRole() { + return apiRole; + } + + public void setApiRole(String apiRole) { + this.apiRole = apiRole; + } + + public String getApplicationRole() { + return applicationRole; + } + + public void setApplicationRole(String applicationRole) { + this.applicationRole = applicationRole; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UpdateInvitationEntity)) return false; + UpdateInvitationEntity that = (UpdateInvitationEntity) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "UpdateInvitationEntity{" + + "id='" + id + '\'' + + ", referenceType=" + referenceType + + ", referenceId='" + referenceId + '\'' + + ", email='" + email + '\'' + + ", apiRole='" + apiRole + '\'' + + ", applicationRole='" + applicationRole + '\'' + + '}'; + } +} diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/GroupPermission.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/GroupPermission.java index 6727f16455..c676cdb95c 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/GroupPermission.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/GroupPermission.java @@ -20,7 +20,8 @@ * @author GraviteeSource Team */ public enum GroupPermission implements Permission { - MEMBER("MEMBER", 1000); + MEMBER("MEMBER", 1000), + INVITATION("INVITATION", 1100); String name; int mask; diff --git a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/RolePermission.java b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/RolePermission.java index 6759f2abcd..82d51c10c6 100644 --- a/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/RolePermission.java +++ b/gravitee-management-api-model/src/main/java/io/gravitee/management/model/permissions/RolePermission.java @@ -71,7 +71,8 @@ public enum RolePermission { APPLICATION_NOTIFICATION(RoleScope.APPLICATION, ApplicationPermission.NOTIFICATION), APPLICATION_ALERT (RoleScope.APPLICATION, ApiPermission.ALERT), - GROUP_MEMBER (RoleScope.GROUP, GroupPermission.MEMBER); + GROUP_MEMBER (RoleScope.GROUP, GroupPermission.MEMBER), + GROUP_INVITATION (RoleScope.GROUP, GroupPermission.INVITATION); RoleScope scope; diff --git a/gravitee-management-api-repository/src/main/java/io/gravitee/management/repository/proxy/InvitationRepositoryProxy.java b/gravitee-management-api-repository/src/main/java/io/gravitee/management/repository/proxy/InvitationRepositoryProxy.java new file mode 100644 index 0000000000..372f67cd08 --- /dev/null +++ b/gravitee-management-api-repository/src/main/java/io/gravitee/management/repository/proxy/InvitationRepositoryProxy.java @@ -0,0 +1,63 @@ +/** + * 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.management.repository.proxy; + +import io.gravitee.repository.exceptions.TechnicalException; +import io.gravitee.repository.management.api.InvitationRepository; +import io.gravitee.repository.management.model.Invitation; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * @author Azize ELAMRANI (azize at graviteesource.com) + * @author GraviteeSource Team + */ +@Component +public class InvitationRepositoryProxy extends AbstractProxy implements InvitationRepository { + + @Override + public Optional findById(String s) throws TechnicalException { + return target.findById(s); + } + + @Override + public Invitation create(Invitation item) throws TechnicalException { + return target.create(item); + } + + @Override + public Invitation update(Invitation item) throws TechnicalException { + return target.update(item); + } + + @Override + public void delete(String s) throws TechnicalException { + target.delete(s); + } + + @Override + public Set findAll() throws TechnicalException { + return target.findAll(); + } + + @Override + public List findByReference(String referenceType, String referenceId) throws TechnicalException { + return target.findByReference(referenceType, referenceId); + } +} diff --git a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupInvitationsResource.java b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupInvitationsResource.java new file mode 100644 index 0000000000..3977e65dcc --- /dev/null +++ b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupInvitationsResource.java @@ -0,0 +1,120 @@ +/** + * 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.management.rest.resource; + +import io.gravitee.common.http.MediaType; +import io.gravitee.management.model.GroupEntity; +import io.gravitee.management.model.InvitationEntity; +import io.gravitee.management.model.NewInvitationEntity; +import io.gravitee.management.model.UpdateInvitationEntity; +import io.gravitee.management.model.permissions.RolePermission; +import io.gravitee.management.model.permissions.RolePermissionAction; +import io.gravitee.management.rest.security.Permission; +import io.gravitee.management.rest.security.Permissions; +import io.gravitee.management.service.GroupService; +import io.gravitee.management.service.InvitationService; +import io.gravitee.management.service.exceptions.GroupInvitationForbiddenException; +import io.gravitee.management.service.exceptions.GroupMembersLimitationExceededException; +import io.gravitee.repository.management.model.MembershipReferenceType; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.ws.rs.DELETE; +import javax.ws.rs.*; +import java.util.List; + +import static io.gravitee.management.model.InvitationReferenceType.GROUP; +import static io.gravitee.management.model.permissions.RolePermission.GROUP_INVITATION; +import static io.gravitee.management.model.permissions.RolePermissionAction.*; +import static io.gravitee.management.service.exceptions.GroupInvitationForbiddenException.Type.EMAIL; +import static io.gravitee.repository.management.model.RoleScope.API; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +@Api(tags = {"Group", "Invitations"}) +public class GroupInvitationsResource extends AbstractResource { + + @Autowired + private InvitationService invitationService; + @Autowired + private GroupService groupService; + + @GET + @ApiOperation(value = "List configured invitations of a given group") + @Produces(MediaType.APPLICATION_JSON) + @Permissions({ + @Permission(value = GROUP_INVITATION, acls = READ) + }) + public List list(@PathParam("group") String group) { + return invitationService.findByReference(GROUP, group); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Permissions({ + @Permission(value = RolePermission.GROUP_INVITATION, acls = RolePermissionAction.CREATE) + }) + public InvitationEntity create(@PathParam("group") String group, @Valid @NotNull final NewInvitationEntity invitationEntity) { + // Check that group exists + final GroupEntity groupEntity = groupService.findById(group); + // check if user is a 'simple group admin' or a platform admin + final boolean hasPermission = permissionService.hasPermission(RolePermission.MANAGEMENT_GROUP, null, CREATE, UPDATE, DELETE); + if (!hasPermission) { + if (groupEntity.getMaxInvitation() != null && + membershipService.getNumberOfMembers(MembershipReferenceType.GROUP, group, API) >= groupEntity.getMaxInvitation()) { + throw new GroupMembersLimitationExceededException(groupEntity.getMaxInvitation()); + } + if (!groupEntity.isEmailInvitation()) { + throw new GroupInvitationForbiddenException(EMAIL, group); + } + } + + invitationEntity.setReferenceType(GROUP); + invitationEntity.setReferenceId(group); + return invitationService.create(invitationEntity); + } + + @Path("{invitation}") + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Permissions({ + @Permission(value = RolePermission.GROUP_INVITATION, acls = RolePermissionAction.UPDATE) + }) + public InvitationEntity update(@PathParam("group") String group, @PathParam("invitation") String invitation, + @Valid @NotNull final UpdateInvitationEntity invitationEntity) { + invitationEntity.setId(invitation); + invitationEntity.setReferenceType(GROUP); + invitationEntity.setReferenceId(group); + return invitationService.update(invitationEntity); + } + + @Path("{invitation}") + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Permissions({ + @Permission(value = RolePermission.GROUP_INVITATION, acls = RolePermissionAction.DELETE) + }) + public void delete(@PathParam("group") String group, @PathParam("invitation") String invitation) { + invitationService.delete(invitation, group); + } +} diff --git a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupMembersResource.java b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupMembersResource.java index 5ce436aa4c..4761651fa9 100644 --- a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupMembersResource.java +++ b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupMembersResource.java @@ -16,10 +16,7 @@ package io.gravitee.management.rest.resource; import io.gravitee.common.http.MediaType; -import io.gravitee.management.model.GroupMemberEntity; -import io.gravitee.management.model.MemberEntity; -import io.gravitee.management.model.MemberRoleEntity; -import io.gravitee.management.model.RoleEntity; +import io.gravitee.management.model.*; import io.gravitee.management.model.permissions.RolePermission; import io.gravitee.management.model.permissions.RolePermissionAction; import io.gravitee.management.rest.model.GroupMembership; @@ -27,7 +24,8 @@ import io.gravitee.management.rest.security.Permissions; import io.gravitee.management.service.GroupService; import io.gravitee.management.service.MembershipService; -import io.gravitee.repository.management.model.MembershipReferenceType; +import io.gravitee.management.service.exceptions.GroupInvitationForbiddenException; +import io.gravitee.management.service.exceptions.GroupMembersLimitationExceededException; import io.gravitee.repository.management.model.RoleScope; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; @@ -43,6 +41,14 @@ import java.util.*; import java.util.stream.Collectors; +import static io.gravitee.management.model.permissions.RolePermission.MANAGEMENT_GROUP; +import static io.gravitee.management.model.permissions.RolePermissionAction.*; +import static io.gravitee.management.service.exceptions.GroupInvitationForbiddenException.Type.SYSTEM; +import static io.gravitee.repository.management.model.MembershipReferenceType.GROUP; +import static io.gravitee.repository.management.model.RoleScope.API; +import static io.gravitee.repository.management.model.RoleScope.APPLICATION; +import static java.util.stream.Collectors.toList; + /** * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com) * @author GraviteeSource Team @@ -51,10 +57,8 @@ public class GroupMembersResource extends AbstractResource { @Context private ResourceContext resourceContext; - @Inject private GroupService groupService; - @Inject private MembershipService membershipService; @@ -65,7 +69,7 @@ public class GroupMembersResource extends AbstractResource { @ApiResponse(code = 200, message = "List of Group's members", response = MemberEntity.class, responseContainer = "List"), @ApiResponse(code = 500, message = "Internal server error")}) @Permissions({ - @Permission(value = RolePermission.MANAGEMENT_GROUP, acls = RolePermissionAction.READ), + @Permission(value = MANAGEMENT_GROUP, acls = RolePermissionAction.READ), @Permission(value = RolePermission.GROUP_MEMBER, acls = RolePermissionAction.READ) }) public List getMembers(@PathParam("group") String group) { @@ -73,19 +77,19 @@ public List getMembers(@PathParam("group") String group) { groupService.findById(group); Map> membersWithApplicationRole = membershipService. - getMembers(MembershipReferenceType.GROUP, group, RoleScope.APPLICATION). + getMembers(GROUP, group, RoleScope.APPLICATION). stream(). filter(Objects::nonNull). collect(Collectors.groupingBy(MemberEntity::getId)); Map> membersWithApiRole = membershipService. - getMembers(MembershipReferenceType.GROUP, group, RoleScope.API). + getMembers(GROUP, group, API). stream(). filter(Objects::nonNull). collect(Collectors.groupingBy(MemberEntity::getId)); Map> membersWithGroupRole = membershipService. - getMembers(MembershipReferenceType.GROUP, group, RoleScope.GROUP). + getMembers(GROUP, group, RoleScope.GROUP). stream(). filter(Objects::nonNull). collect(Collectors.groupingBy(MemberEntity::getId)); @@ -102,7 +106,7 @@ public List getMembers(@PathParam("group") String group) { GroupMemberEntity groupMemberEntity = new GroupMemberEntity(Objects.nonNull(memberWithApiRole) ? memberWithApiRole : memberWithApplicationRole); groupMemberEntity.setRoles(new HashMap<>()); if (Objects.nonNull(memberWithApiRole)) { - groupMemberEntity.getRoles().put(RoleScope.API.name(), memberWithApiRole.getRole()); + groupMemberEntity.getRoles().put(API.name(), memberWithApiRole.getRole()); } if (Objects.nonNull(memberWithApplicationRole)) { groupMemberEntity.getRoles().put(RoleScope.APPLICATION.name(), memberWithApplicationRole.getRole()); @@ -113,7 +117,7 @@ public List getMembers(@PathParam("group") String group) { return groupMemberEntity; }). sorted(Comparator.comparing(GroupMemberEntity::getId)). - collect(Collectors.toList()); + collect(toList()); } @POST @@ -127,8 +131,8 @@ public List getMembers(@PathParam("group") String group) { @ApiResponse(code = 500, message = "Internal server error") }) @Permissions({ - @Permission(value = RolePermission.MANAGEMENT_GROUP, acls = RolePermissionAction.CREATE), - @Permission(value = RolePermission.MANAGEMENT_GROUP, acls = RolePermissionAction.UPDATE), + @Permission(value = MANAGEMENT_GROUP, acls = RolePermissionAction.CREATE), + @Permission(value = MANAGEMENT_GROUP, acls = RolePermissionAction.UPDATE), @Permission(value = RolePermission.GROUP_MEMBER, acls = RolePermissionAction.CREATE), @Permission(value = RolePermission.GROUP_MEMBER, acls = RolePermissionAction.UPDATE), }) @@ -137,26 +141,44 @@ public Response addOrUpdateMember( @Valid @NotNull final List memberships ) { // Check that group exists - groupService.findById(group); + final GroupEntity groupEntity = groupService.findById(group); + + // check if user is a 'simple group admin' or a platform admin + final boolean hasPermission = permissionService.hasPermission(MANAGEMENT_GROUP, null, CREATE, UPDATE, DELETE); + if (!hasPermission) { + if (groupEntity.getMaxInvitation() != null) { + final Set members = membershipService.getMembers(GROUP, group, RoleScope.API); + final long membershipsToAddSize = memberships.stream().map(GroupMembership::getId).filter(s -> { + final List membershipIdsToSave = members.stream().map(MemberEntity::getId).collect(toList()); + return !membershipIdsToSave.contains(s); + }).count(); + if ((membershipService.getNumberOfMembers(GROUP, group, API) + membershipsToAddSize) > groupEntity.getMaxInvitation()) { + throw new GroupMembersLimitationExceededException(groupEntity.getMaxInvitation()); + } + } + if (!groupEntity.isSystemInvitation()) { + throw new GroupInvitationForbiddenException(SYSTEM, group); + } + } for (GroupMembership membership : memberships) { RoleEntity previousApiRole = null, previousApplicationRole = null, previousGroupRole = null; if (membership.getId() != null) { previousApiRole = membershipService.getRole( - MembershipReferenceType.GROUP, + GROUP, group, membership.getId(), - RoleScope.API); + API); previousApplicationRole = membershipService.getRole( - MembershipReferenceType.GROUP, + GROUP, group, membership.getId(), RoleScope.APPLICATION); previousGroupRole = membershipService.getRole( - MembershipReferenceType.GROUP, + GROUP, group, membership.getId(), RoleScope.GROUP); @@ -189,20 +211,34 @@ public Response addOrUpdateMember( // Add / Update if (apiRole != null) { + String roleName = apiRole.getRoleName(); + if (!hasPermission && groupEntity.isLockApiRole()) { + final List defaultRoles = roleService.findDefaultRoleByScopes(API); + if (defaultRoles != null && !defaultRoles.isEmpty()) { + roleName = defaultRoles.get(0).getName(); + } + } updatedMembership = membershipService.addOrUpdateMember( - new MembershipService.MembershipReference(MembershipReferenceType.GROUP, group), + new MembershipService.MembershipReference(GROUP, group), new MembershipService.MembershipUser(membership.getId(), membership.getReference()), - new MembershipService.MembershipRole(RoleScope.API, apiRole.getRoleName())); + new MembershipService.MembershipRole(API, roleName)); } if (applicationRole != null) { + String roleName = applicationRole.getRoleName(); + if (!hasPermission && groupEntity.isLockApplicationRole()) { + final List defaultRoles = roleService.findDefaultRoleByScopes(APPLICATION); + if (defaultRoles != null && !defaultRoles.isEmpty()) { + roleName = defaultRoles.get(0).getName(); + } + } updatedMembership = membershipService.addOrUpdateMember( - new MembershipService.MembershipReference(MembershipReferenceType.GROUP, group), + new MembershipService.MembershipReference(GROUP, group), new MembershipService.MembershipUser(membership.getId(), membership.getReference()), - new MembershipService.MembershipRole(RoleScope.APPLICATION, applicationRole.getRoleName())); + new MembershipService.MembershipRole(RoleScope.APPLICATION, roleName)); } if (groupRole != null) { updatedMembership = membershipService.addOrUpdateMember( - new MembershipService.MembershipReference(MembershipReferenceType.GROUP, group), + new MembershipService.MembershipReference(GROUP, group), new MembershipService.MembershipUser(membership.getId(), membership.getReference()), new MembershipService.MembershipRole(RoleScope.GROUP, groupRole.getRoleName())); } @@ -210,21 +246,21 @@ public Response addOrUpdateMember( // Delete if (apiRole == null && previousApiRole != null) { membershipService.removeRole( - MembershipReferenceType.GROUP, + GROUP, group, updatedMembership.getId(), - RoleScope.API); + API); } if (applicationRole == null && previousApplicationRole != null) { membershipService.removeRole( - MembershipReferenceType.GROUP, + GROUP, group, updatedMembership.getId(), RoleScope.APPLICATION); } if (groupRole == null && previousGroupRole != null) { membershipService.removeRole( - MembershipReferenceType.GROUP, + GROUP, group, updatedMembership.getId(), RoleScope.GROUP); diff --git a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupResource.java b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupResource.java index fe666224f1..56d1be6102 100644 --- a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupResource.java +++ b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/GroupResource.java @@ -17,14 +17,15 @@ import io.gravitee.management.model.GroupEntity; import io.gravitee.management.model.UpdateGroupEntity; -import io.gravitee.management.model.api.ApiQuery; import io.gravitee.management.model.permissions.RolePermission; import io.gravitee.management.model.permissions.RolePermissionAction; +import io.gravitee.management.model.permissions.RoleScope; import io.gravitee.management.rest.security.Permission; import io.gravitee.management.rest.security.Permissions; import io.gravitee.management.service.ApiService; import io.gravitee.management.service.ApplicationService; import io.gravitee.management.service.GroupService; +import io.gravitee.management.service.exceptions.ForbiddenAccessException; import io.swagger.annotations.*; import javax.inject.Inject; @@ -33,12 +34,10 @@ import javax.ws.rs.*; import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.Collections; - import static io.gravitee.common.http.MediaType.APPLICATION_JSON; +import static io.gravitee.management.model.permissions.RolePermissionAction.*; /** * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com) @@ -83,6 +82,7 @@ public GroupEntity get(@PathParam("group") String group) { @Permission(value = RolePermission.MANAGEMENT_GROUP, acls = RolePermissionAction.DELETE) }) public Response delete(@PathParam("group") String group) { + checkRights(group); groupService.delete(group); return Response.noContent().build(); } @@ -99,8 +99,23 @@ public Response delete(@PathParam("group") String group) { @Permission(value = RolePermission.GROUP_MEMBER, acls = RolePermissionAction.UPDATE) }) public GroupEntity update( - @PathParam("group")String group, - @ApiParam(name = "group", required = true)@Valid @NotNull final UpdateGroupEntity updateGroupEntity) { + @PathParam("group") String group, + @ApiParam(name = "group", required = true) @Valid @NotNull final UpdateGroupEntity updateGroupEntity) { + final GroupEntity groupEntity = checkRights(group); + // check if user is a 'simple group admin' or a platform admin + if (!permissionService.hasPermission(RolePermission.MANAGEMENT_GROUP, null, CREATE, UPDATE, DELETE)) { + updateGroupEntity.setMaxInvitation(groupEntity.getMaxInvitation()); + updateGroupEntity.setLockApiRole(groupEntity.isLockApiRole()); + updateGroupEntity.setLockApplicationRole(groupEntity.isLockApplicationRole()); + updateGroupEntity.setSystemInvitation(groupEntity.isSystemInvitation()); + updateGroupEntity.setEmailInvitation(groupEntity.isEmailInvitation()); + if (groupEntity.isLockApiRole()) { + updateGroupEntity.getRoles().put(RoleScope.API, groupEntity.getRoles().get(RoleScope.API)); + } + if (groupEntity.isLockApplicationRole()) { + updateGroupEntity.getRoles().put(RoleScope.APPLICATION, groupEntity.getRoles().get(RoleScope.APPLICATION)); + } + } return groupService.update(group, updateGroupEntity); } @@ -125,9 +140,21 @@ public Response getMemberships( return Response.noContent().build(); } + private GroupEntity checkRights(final String group) { + final GroupEntity groupEntity = get(group); + if (!groupEntity.isManageable()) { + throw new ForbiddenAccessException(); + } + return groupEntity; + } + @Path("members") public GroupMembersResource groupMembersResource() { return resourceContext.getResource(GroupMembersResource.class); } + @Path("invitations") + public GroupInvitationsResource groupInvitationsResource() { + return resourceContext.getResource(GroupInvitationsResource.class); + } } diff --git a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/UsersResource.java b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/UsersResource.java index ec510a458b..69c563d936 100644 --- a/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/UsersResource.java +++ b/gravitee-management-api-rest/src/main/java/io/gravitee/management/rest/resource/UsersResource.java @@ -29,7 +29,6 @@ import javax.inject.Inject; import javax.validation.Valid; -import javax.validation.constraints.NotNull; import javax.ws.rs.*; import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; @@ -92,7 +91,7 @@ public Response registerUser(@Valid NewExternalUserEntity newExternalUserEntity) return Response.serverError().build(); } - + @POST public Response createUser(@Valid RegisterUserEntity registerUserEntity) { UserEntity newUser = userService.create(registerUserEntity); diff --git a/gravitee-management-api-rest/src/test/java/io/gravitee/management/rest/resource/GroupMemberResourceTest.java b/gravitee-management-api-rest/src/test/java/io/gravitee/management/rest/resource/GroupMembersResourceTest.java similarity index 94% rename from gravitee-management-api-rest/src/test/java/io/gravitee/management/rest/resource/GroupMemberResourceTest.java rename to gravitee-management-api-rest/src/test/java/io/gravitee/management/rest/resource/GroupMembersResourceTest.java index c7cdd1e318..19bd3f3553 100644 --- a/gravitee-management-api-rest/src/test/java/io/gravitee/management/rest/resource/GroupMemberResourceTest.java +++ b/gravitee-management-api-rest/src/test/java/io/gravitee/management/rest/resource/GroupMembersResourceTest.java @@ -16,10 +16,12 @@ package io.gravitee.management.rest.resource; import io.gravitee.common.http.HttpStatusCode; -import io.gravitee.management.model.*; +import io.gravitee.management.model.GroupEntity; +import io.gravitee.management.model.MemberEntity; +import io.gravitee.management.model.MemberRoleEntity; +import io.gravitee.management.model.RoleEntity; import io.gravitee.management.rest.model.GroupMembership; import io.gravitee.management.service.MembershipService; -import io.gravitee.repository.management.model.Membership; import io.gravitee.repository.management.model.MembershipReferenceType; import io.gravitee.repository.management.model.RoleScope; import org.junit.Test; @@ -29,6 +31,8 @@ import java.util.Arrays; import java.util.Collections; +import static io.gravitee.management.model.permissions.RolePermission.MANAGEMENT_GROUP; +import static io.gravitee.management.model.permissions.RolePermissionAction.*; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; @@ -36,7 +40,7 @@ * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com) * @author GraviteeSource Team */ -public class GroupMemberResourceTest extends AbstractResourceTest { +public class GroupMembersResourceTest extends AbstractResourceTest { private static final String GROUP_ID = "group-id"; private static final String USERNAME = "user"; @@ -63,6 +67,7 @@ private void initADDmock() { MemberEntity memberEntity = new MemberEntity(); memberEntity.setId(USERNAME); when(membershipService.addOrUpdateMember(any(), any(), any())).thenReturn(memberEntity); + when(permissionService.hasPermission(MANAGEMENT_GROUP, null, CREATE, UPDATE, DELETE)).thenReturn(true); } @Test diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/InvitationService.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/InvitationService.java new file mode 100644 index 0000000000..1f7871b2f2 --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/InvitationService.java @@ -0,0 +1,36 @@ +/** + * 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.management.service; + +import io.gravitee.management.model.InvitationEntity; +import io.gravitee.management.model.InvitationReferenceType; +import io.gravitee.management.model.NewInvitationEntity; +import io.gravitee.management.model.UpdateInvitationEntity; + +import java.util.List; + +/** + * @author Azize ELAMRANI (azize at graviteesource.com) + * @author GraviteeSource Team + */ +public interface InvitationService { + InvitationEntity create(NewInvitationEntity invitation); + InvitationEntity update(UpdateInvitationEntity invitation); + List findByReference(InvitationReferenceType referenceType, String referenceId); + void delete(String invitationId, String referenceId); + List findAll(); + void addMember(String referenceType, String referenceId, String userId, String apiRole, String applicationRole); +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/MembershipService.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/MembershipService.java index e0da48e487..081a424049 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/MembershipService.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/MembershipService.java @@ -32,37 +32,23 @@ public interface MembershipService { MemberEntity getMember(MembershipReferenceType referenceType, String referenceId, String userId, RoleScope roleScope); - RoleEntity getRole(MembershipReferenceType referenceType, String referenceId, String userId, RoleScope roleScope); - Set getRoles(MembershipReferenceType referenceType, Set referenceIds, String userId, RoleScope roleScope); - Set getMembers(MembershipReferenceType referenceType, String referenceId, RoleScope roleScope); - Set getMembers(MembershipReferenceType referenceType, String referenceId, RoleScope roleScope, String roleName); - MemberEntity addOrUpdateMember(MembershipReference reference, MembershipUser user, MembershipRole role); - void deleteMember(MembershipReferenceType referenceType, String referenceId, String userId); - void transferApiOwnership(String apiId, MembershipUser user, RoleEntity newPrimaryOwnerRole); - void transferApplicationOwnership(String applicationId, MembershipUser user, RoleEntity newPrimaryOwnerRole); - Map getMemberPermissions(ApiEntity api, String userId); - Map getMemberPermissions(ApplicationEntity application, String userId); - Map getMemberPermissions(GroupEntity group, String userId); - boolean removeRole(MembershipReferenceType referenceType, String referenceId, String userId, RoleScope roleScope); - void removeRoleUsage(RoleScope roleScope, String roleName, String newName); - void removeUser(String userId); - List findUserMembership(String userId, MembershipReferenceType type); Metadata findUserMembershipMetadata(List memberships, MembershipReferenceType type); + int getNumberOfMembers(MembershipReferenceType referenceType, String referenceId, RoleScope roleScope); class MembershipReference { private final MembershipReferenceType type; diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/UserService.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/UserService.java index eb0a40cead..233f631811 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/UserService.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/UserService.java @@ -18,8 +18,10 @@ import io.gravitee.common.data.domain.Page; import io.gravitee.management.model.*; import io.gravitee.management.model.common.Pageable; +import io.gravitee.management.service.common.JWTHelper.ACTION; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -43,4 +45,5 @@ public interface UserService { PictureEntity getPicture(String id); void delete(String id); void resetPassword(String id); + Map getTokenRegistrationParams(UserEntity userEntity, String portalUri, ACTION action); } diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/builder/EmailNotificationBuilder.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/builder/EmailNotificationBuilder.java index f19386d94c..6126172eb2 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/builder/EmailNotificationBuilder.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/builder/EmailNotificationBuilder.java @@ -17,6 +17,7 @@ import io.gravitee.management.service.EmailNotification; +import java.util.HashMap; import java.util.Map; /** @@ -53,6 +54,9 @@ public EmailNotificationBuilder subject(String subject) { } public EmailNotificationBuilder param(String key, Object value) { + if (this.emailNotification.getParams() == null) { + this.emailNotification.setParams(new HashMap<>()); + } this.emailNotification.getParams().put(key, value); return this; } @@ -101,7 +105,8 @@ public enum EmailTemplate { API_STOPPED("apiStopped.html"), NEW_RATING("newRating.html"), NEW_RATING_ANSWER("newRatingAnswer.html"), - GENERIC_MESSAGE("genericMessage.html"); + GENERIC_MESSAGE("genericMessage.html"), + GROUP_INVITATION("groupInvitation.html"); private String template; diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/common/JWTHelper.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/common/JWTHelper.java index cf0be1d8ec..6459d7a17f 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/common/JWTHelper.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/common/JWTHelper.java @@ -21,6 +21,10 @@ */ public interface JWTHelper { + public enum ACTION { + RESET_PASSWORD, USER_REGISTRATION, GROUP_INVITATION + } + interface Claims { String ISSUER = "iss"; String SUBJECT = "sub"; @@ -28,6 +32,7 @@ interface Claims { String EMAIL = "email"; String FIRSTNAME = "firstname"; String LASTNAME = "lastname"; + String ACTION = "action"; } interface DefaultValues { diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/EmailDisabledException.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/EmailDisabledException.java new file mode 100644 index 0000000000..531fdf7864 --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/EmailDisabledException.java @@ -0,0 +1,35 @@ +/** + * 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.management.service.exceptions; + +import static io.gravitee.common.http.HttpStatusCode.SERVICE_UNAVAILABLE_503; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public class EmailDisabledException extends AbstractManagementException { + + @Override + public int getHttpStatusCode() { + return SERVICE_UNAVAILABLE_503; + } + + @Override + public String getMessage() { + return "Email service is disabled"; + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/GroupInvitationForbiddenException.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/GroupInvitationForbiddenException.java new file mode 100644 index 0000000000..8879444c8a --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/GroupInvitationForbiddenException.java @@ -0,0 +1,38 @@ +/** + * 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.management.service.exceptions; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public class GroupInvitationForbiddenException extends AbstractNotFoundException { + + public enum Type {EMAIL,SYSTEM} + + private final Type type; + private final String group; + + public GroupInvitationForbiddenException(Type type, String group) { + this.type = type; + this.group = group; + } + + @Override + public String getMessage() { + return "Invitation " + type.name().toLowerCase() + " is forbidden for group [" + group + "]"; + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/GroupMembersLimitationExceededException.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/GroupMembersLimitationExceededException.java new file mode 100644 index 0000000000..596490f18e --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/GroupMembersLimitationExceededException.java @@ -0,0 +1,43 @@ +/** + * 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.management.service.exceptions; + +import io.gravitee.common.http.HttpStatusCode; + +import static java.lang.String.format; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public class GroupMembersLimitationExceededException extends AbstractManagementException { + + private final int limit; + + public GroupMembersLimitationExceededException(int limit) { + this.limit = limit; + } + + @Override + public int getHttpStatusCode() { + return HttpStatusCode.BAD_REQUEST_400; + } + + @Override + public String getMessage() { + return format("Limitation of %d members exceeded", limit); + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/InvitationEmailAlreadyExistsException.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/InvitationEmailAlreadyExistsException.java new file mode 100644 index 0000000000..0b9a3ef698 --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/InvitationEmailAlreadyExistsException.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.management.service.exceptions; + +import io.gravitee.common.http.HttpStatusCode; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public class InvitationEmailAlreadyExistsException extends AbstractManagementException { + + private final String email; + + public InvitationEmailAlreadyExistsException(String email) { + this.email = email; + } + + @Override + public int getHttpStatusCode() { + return HttpStatusCode.BAD_REQUEST_400; + } + + @Override + public String getMessage() { + return "An invitation with the email [" + email + "] already exists."; + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/InvitationNotFoundException.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/InvitationNotFoundException.java new file mode 100644 index 0000000000..30a3d666f8 --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/InvitationNotFoundException.java @@ -0,0 +1,34 @@ +/** + * 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.management.service.exceptions; + +/** + * @author Azize ELAMRANI (azize at graviteesource.com) + * @author GraviteeSource Team + */ +public class InvitationNotFoundException extends AbstractNotFoundException { + + private final String invitation; + + public InvitationNotFoundException(String invitation) { + this.invitation = invitation; + } + + @Override + public String getMessage() { + return "Invitation [" + invitation + "] can not be found"; + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/MemberEmailAlreadyExistsException.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/MemberEmailAlreadyExistsException.java new file mode 100644 index 0000000000..cf7b828e8c --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/exceptions/MemberEmailAlreadyExistsException.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.management.service.exceptions; + +import io.gravitee.common.http.HttpStatusCode; + +/** + * @author Azize ELAMRANI (azize.elamrani at graviteesource.com) + * @author GraviteeSource Team + */ +public class MemberEmailAlreadyExistsException extends AbstractManagementException { + + private final String email; + + public MemberEmailAlreadyExistsException(String email) { + this.email = email; + } + + @Override + public int getHttpStatusCode() { + return HttpStatusCode.BAD_REQUEST_400; + } + + @Override + public String getMessage() { + return "A member with the email [" + email + "] already exists."; + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/EmailServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/EmailServiceImpl.java index 3c31d7c794..dd22971070 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/EmailServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/EmailServiceImpl.java @@ -19,6 +19,7 @@ import freemarker.template.Template; import io.gravitee.management.service.EmailNotification; import io.gravitee.management.service.EmailService; +import io.gravitee.management.service.exceptions.EmailDisabledException; import io.gravitee.management.service.exceptions.TechnicalManagementException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -105,6 +106,8 @@ public void sendEmailNotification(final EmailNotification emailNotification) { LOGGER.error("Error while sending email notification", ex); throw new TechnicalManagementException("Error while sending email notification", ex); } + } else { + throw new EmailDisabledException(); } } diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/GroupServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/GroupServiceImpl.java index e51b8bfd80..b4d9564caa 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/GroupServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/GroupServiceImpl.java @@ -59,22 +59,16 @@ public class GroupServiceImpl extends AbstractService implements GroupService { @Autowired private GroupRepository groupRepository; - @Autowired private ApiRepository apiRepository; - @Autowired private ApplicationRepository applicationRepository; - @Autowired private MembershipRepository membershipRepository; - @Autowired private MembershipService membershipService; - @Autowired private AuditService auditService; - @Autowired private PermissionService permissionService; @@ -84,13 +78,13 @@ public List findAll() { logger.debug("Find all groups"); Set all = groupRepository.findAll(); logger.debug("Find all groups - DONE"); - List result = all.stream() + final List groups = all.stream() .map(this::map) .sorted(Comparator.comparing(GroupEntity::getName)) .collect(Collectors.toList()); if (permissionService.hasPermission(RolePermission.MANAGEMENT_GROUP, null, CREATE, UPDATE, DELETE)) { - result.forEach(groupEntity -> groupEntity.setManageable(true)); + groups.forEach(groupEntity -> groupEntity.setManageable(true)); } else { List groupIds = membershipRepository.findByUserAndReferenceTypeAndRole( getAuthenticatedUsername(), @@ -100,9 +94,9 @@ public List findAll() { .stream() .map(Membership::getReferenceId) .collect(Collectors.toList()); - result.forEach(groupEntity -> groupEntity.setManageable(groupIds.contains(groupEntity.getId()))); + groups.forEach(groupEntity -> groupEntity.setManageable(groupIds.contains(groupEntity.getId()))); } - return result; + return groups; } catch (TechnicalException ex) { logger.error("An error occurs while trying to find all groups", ex); throw new TechnicalManagementException("An error occurs while trying to find all groups", ex); @@ -166,6 +160,11 @@ public GroupEntity update(String groupId, UpdateGroupEntity group) { updatedGroupEntity.setUpdatedAt(new Date()); updatedGroupEntity.setEventRules(group.getEventRules()); updatedGroupEntity.setRoles(group.getRoles()); + updatedGroupEntity.setMaxInvitation(group.getMaxInvitation()); + updatedGroupEntity.setLockApiRole(group.isLockApiRole()); + updatedGroupEntity.setLockApplicationRole(group.isLockApplicationRole()); + updatedGroupEntity.setSystemInvitation(group.isSystemInvitation()); + updatedGroupEntity.setEmailInvitation(group.isEmailInvitation()); Group updatedGroup = this.map(updatedGroupEntity); GroupEntity grp = this.map(groupRepository.update(updatedGroup)); @@ -178,7 +177,7 @@ public GroupEntity update(String groupId, UpdateGroupEntity group) { updatedGroupEntity.getUpdatedAt(), previousGroup, updatedGroup); - return grp; + return findById(groupId); } catch (TechnicalException ex) { logger.error("An error occurs while trying to update a group", ex); throw new TechnicalManagementException("An error occurs while trying to update a group", ex); @@ -476,6 +475,11 @@ private Group map(GroupEntity entity) { group.setCreatedAt(entity.getCreatedAt()); group.setUpdatedAt(entity.getUpdatedAt()); + group.setMaxInvitation(entity.getMaxInvitation()); + group.setLockApiRole(entity.isLockApiRole()); + group.setLockApplicationRole(entity.isLockApplicationRole()); + group.setSystemInvitation(entity.isSystemInvitation()); + group.setEmailInvitation(entity.isEmailInvitation()); return group; } @@ -496,6 +500,11 @@ private Group map(NewGroupEntity entity) { } group.setEventRules(groupEventRules); } + entity.setMaxInvitation(group.getMaxInvitation()); + entity.setLockApiRole(group.isLockApiRole()); + entity.setLockApplicationRole(group.isLockApplicationRole()); + entity.setSystemInvitation(group.isSystemInvitation()); + entity.setEmailInvitation(group.isEmailInvitation()); return group; } @@ -531,6 +540,11 @@ private GroupEntity map(Group group) { entity.setCreatedAt(group.getCreatedAt()); entity.setUpdatedAt(group.getUpdatedAt()); + entity.setMaxInvitation(group.getMaxInvitation()); + entity.setLockApiRole(group.isLockApiRole()); + entity.setLockApplicationRole(group.isLockApplicationRole()); + entity.setSystemInvitation(group.isSystemInvitation()); + entity.setEmailInvitation(group.isEmailInvitation()); return entity; } diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/InvitationServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/InvitationServiceImpl.java new file mode 100644 index 0000000000..8d2a5083a3 --- /dev/null +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/InvitationServiceImpl.java @@ -0,0 +1,247 @@ +/** + * 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.management.service.impl; + +import io.gravitee.common.data.domain.Page; +import io.gravitee.common.utils.UUID; +import io.gravitee.management.model.*; +import io.gravitee.management.model.common.PageableImpl; +import io.gravitee.management.model.permissions.RolePermission; +import io.gravitee.management.service.*; +import io.gravitee.management.service.MembershipService.MembershipReference; +import io.gravitee.management.service.builder.EmailNotificationBuilder; +import io.gravitee.management.service.common.JWTHelper; +import io.gravitee.management.service.exceptions.InvitationEmailAlreadyExistsException; +import io.gravitee.management.service.exceptions.InvitationNotFoundException; +import io.gravitee.management.service.exceptions.MemberEmailAlreadyExistsException; +import io.gravitee.management.service.exceptions.TechnicalManagementException; +import io.gravitee.repository.exceptions.TechnicalException; +import io.gravitee.repository.management.api.InvitationRepository; +import io.gravitee.repository.management.model.Invitation; +import io.gravitee.repository.management.model.MembershipReferenceType; +import io.gravitee.repository.management.model.RoleScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static io.gravitee.management.model.permissions.RolePermissionAction.*; +import static io.gravitee.management.service.common.JWTHelper.ACTION.GROUP_INVITATION; +import static io.gravitee.management.service.notification.NotificationParamsBuilder.REGISTRATION_PATH; +import static io.gravitee.repository.management.model.RoleScope.*; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +/** + * @author Azize ELAMRANI (azize at graviteesource.com) + * @author GraviteeSource Team + */ +@Component +public class InvitationServiceImpl extends TransactionalService implements InvitationService { + + private final Logger LOGGER = LoggerFactory.getLogger(InvitationServiceImpl.class); + + @Autowired + private InvitationRepository invitationRepository; + @Autowired + private EmailService emailService; + @Autowired + private UserService userService; + @Autowired + private MembershipService membershipService; + @Autowired + private RoleService roleService; + @Autowired + private GroupService groupService; + @Autowired PermissionService permissionService; + + @Override + public InvitationEntity create(final NewInvitationEntity invitation) { + final List invitations = findByReference(invitation.getReferenceType(), invitation.getReferenceId()); + if (invitations.stream().map(InvitationEntity::getEmail).anyMatch(invitation.getEmail()::equals)) { + throw new InvitationEmailAlreadyExistsException(invitation.getEmail()); + } + try { + // First check if user exists + final Page pageUser = userService.search(invitation.getEmail(), new PageableImpl(1, 2)); + if (pageUser.getTotalElements() == 1) { + final Set members = membershipService.getMembers( + MembershipReferenceType.valueOf(invitation.getReferenceType().name()), invitation.getReferenceId(), API); + if (members.stream().map(MemberEntity::getEmail).anyMatch(invitation.getEmail()::equals)) { + throw new MemberEmailAlreadyExistsException(invitation.getEmail()); + } + // override permission if not allowed + final boolean hasPermission = permissionService.hasPermission(RolePermission.MANAGEMENT_GROUP, null, CREATE, UPDATE, DELETE); + final GroupEntity group = groupService.findById(invitation.getReferenceId()); + if (!hasPermission && group.isLockApiRole()) { + invitation.setApiRole(null); + } + if (!hasPermission && group.isLockApplicationRole()) { + invitation.setApplicationRole(null); + } + + final UserEntity user = pageUser.getContent().get(0); + addMember(invitation.getReferenceType().name(), invitation.getReferenceId(), user.getId(), + invitation.getApiRole(), invitation.getApplicationRole()); + return null; + } else { + sendGroupInvitationEmail(invitation); + final Invitation createdInvitation = invitationRepository.create(convert(invitation)); + return convert(createdInvitation); + } + } catch (TechnicalException ex) { + final String message = "An error occurs while trying to create invitation for email " + invitation.getEmail(); + LOGGER.error(message, ex); + throw new TechnicalManagementException(message, ex); + } + } + + @Override + public InvitationEntity update(final UpdateInvitationEntity invitation) { + try { + final Optional invitationOptional = invitationRepository.findById(invitation.getId()); + if (invitationOptional.isPresent()) { + final Invitation invitationToUpdate = invitationOptional.get(); + if (!invitationToUpdate.getReferenceId().equals(invitation.getReferenceId())) { + throw new InvitationNotFoundException(invitation.getId()); + } + + invitationToUpdate.setApiRole(invitation.getApiRole()); + invitationToUpdate.setApplicationRole(invitation.getApplicationRole()); + invitationToUpdate.setUpdatedAt(new Date()); + return convert(invitationRepository.update(invitationToUpdate)); + } else { + throw new InvitationNotFoundException(invitation.getId()); + } + } catch (TechnicalException ex) { + final String message = "An error occurs while trying to update invitation with email " + invitation.getEmail(); + LOGGER.error(message, ex); + throw new TechnicalManagementException(message, ex); + } + } + + @Override + public void addMember(final String referenceType, final String referenceId, final String userId, + final String apiRole, final String applicationRole) { + addOrUpdateMemberByScope(referenceType, referenceId, userId, API, apiRole); + addOrUpdateMemberByScope(referenceType, referenceId, userId, APPLICATION, applicationRole); + addOrUpdateMemberByScope(referenceType, referenceId, userId, GROUP, null); + } + + private void addOrUpdateMemberByScope(final String referenceType, final String referenceId, final String userId, + final RoleScope roleScope, final String defaultRole) { + String defaultRoleName = null; + if (defaultRole == null) { + final List defaultRoles = roleService.findDefaultRoleByScopes(roleScope); + if (defaultRoles != null && !defaultRoles.isEmpty()) { + defaultRoleName = defaultRoles.get(0).getName(); + } + } else { + defaultRoleName = defaultRole; + } + if (defaultRoleName != null) { + membershipService.addOrUpdateMember( + new MembershipReference(MembershipReferenceType.valueOf(referenceType), referenceId), + new MembershipService.MembershipUser(userId, null), + new MembershipService.MembershipRole(roleScope, defaultRoleName) + ); + } + } + + private void sendGroupInvitationEmail(NewInvitationEntity invitation) { + final UserEntity userEntity = new UserEntity(); + userEntity.setEmail(invitation.getEmail()); + final GroupEntity group = groupService.findById(invitation.getReferenceId()); + emailService.sendEmailNotification(new EmailNotificationBuilder() + .to(invitation.getEmail()) + .subject("Group invitation - " + group.getName()) + .template(EmailNotificationBuilder.EmailTemplate.GROUP_INVITATION) + .params(userService.getTokenRegistrationParams(userEntity, REGISTRATION_PATH, GROUP_INVITATION)) + .param("group", group) + .build() + ); + } + + @Override + public List findAll() { + try { + final Set invitations = invitationRepository.findAll(); + return invitations.stream().map(this::convert).collect(toList()); + } catch (TechnicalException ex) { + final String message = "An error occurs while trying to list all invitations"; + LOGGER.error(message, ex); + throw new TechnicalManagementException(message, ex); + } + } + + @Override + public List findByReference(final InvitationReferenceType referenceType, final String referenceId) { + try { + final List invitations = invitationRepository.findByReference(referenceType.name(), referenceId); + return invitations.stream().map(this::convert).sorted(comparing(InvitationEntity::getEmail)).collect(toList()); + } catch (TechnicalException ex) { + final String message = "An error occurs while trying to list invitations by reference " + referenceType + + '/' + referenceId; + LOGGER.error(message, ex); + throw new TechnicalManagementException(message, ex); + } + } + + @Override + public void delete(final String invitationId, final String referenceId) { + try { + final Optional optionalInvitation = invitationRepository.findById(invitationId); + if (!optionalInvitation.isPresent() || !optionalInvitation.get().getReferenceId().equals(referenceId)) { + throw new InvitationNotFoundException(invitationId); + } + invitationRepository.delete(invitationId); + } catch (TechnicalException te) { + final String msg = "An error occurs while trying to delete the invitation " + invitationId; + LOGGER.error(msg, te); + throw new TechnicalManagementException(msg, te); + } + } + + private Invitation convert(final NewInvitationEntity invitationEntity) { + final Invitation invitation = new Invitation(); + invitation.setId(UUID.toString(UUID.random())); + invitation.setReferenceId(invitationEntity.getReferenceId()); + invitation.setReferenceType(invitationEntity.getReferenceType().name()); + invitation.setApiRole(invitationEntity.getApiRole()); + invitation.setApplicationRole(invitationEntity.getApplicationRole()); + invitation.setEmail(invitationEntity.getEmail()); + final Date now = new Date(); + invitation.setCreatedAt(now); + return invitation; + } + + private InvitationEntity convert(final Invitation invitation) { + final InvitationEntity invitationEntity = new InvitationEntity(); + invitationEntity.setId(invitation.getId()); + invitationEntity.setReferenceId(invitation.getReferenceId()); + invitationEntity.setReferenceType(InvitationReferenceType.valueOf(invitation.getReferenceType())); + invitationEntity.setApiRole(invitation.getApiRole()); + invitationEntity.setApplicationRole(invitation.getApplicationRole()); + invitationEntity.setEmail(invitation.getEmail()); + invitationEntity.setCreatedAt(invitation.getCreatedAt()); + return invitationEntity; + } +} diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/MembershipServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/MembershipServiceImpl.java index ff61d9dd1c..042ec5e6c6 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/MembershipServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/MembershipServiceImpl.java @@ -28,7 +28,6 @@ import io.gravitee.repository.exceptions.TechnicalException; import io.gravitee.repository.management.api.ApiRepository; import io.gravitee.repository.management.api.ApplicationRepository; -import io.gravitee.repository.management.api.GroupRepository; import io.gravitee.repository.management.api.MembershipRepository; import io.gravitee.repository.management.api.search.ApiCriteria; import io.gravitee.repository.management.api.search.ApiFieldExclusionFilter; @@ -45,8 +44,10 @@ import java.util.stream.Collectors; import static io.gravitee.management.model.permissions.SystemRole.PRIMARY_OWNER; +import static io.gravitee.management.service.notification.PortalHook.GROUP_INVITATION; import static io.gravitee.repository.management.model.Membership.AuditEvent.*; import static io.gravitee.repository.management.model.MembershipReferenceType.*; +import static java.util.Collections.singletonMap; /** * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com) @@ -59,42 +60,30 @@ public class MembershipServiceImpl extends AbstractService implements Membership @Autowired private UserService userService; - @Autowired private EmailService emailService; - @Autowired private IdentityService identityService; - @Autowired private MembershipRepository membershipRepository; - @Autowired private RoleService roleService; - @Autowired private ApplicationService applicationService; - @Autowired private ApiService apiService; - @Autowired private GroupService groupService; - @Autowired private AuditService auditService; - - @Autowired - private NotifierService notifierService; - - @Autowired - private GroupRepository groupRepository; - @Autowired private ApiRepository apiRepository; - @Autowired private ApplicationRepository applicationRepository; + @Autowired + private NotifierService notifierService; + @Autowired + private InvitationService invitationService; @Override public Set getMembers(MembershipReferenceType referenceType, String referenceId, RoleScope roleScope) { @@ -288,18 +277,20 @@ public MemberEntity addOrUpdateMember(MembershipReference reference, MembershipU createAuditLog(MEMBERSHIP_UPDATED, updatedMembership.getUpdatedAt(), previousMembership, updatedMembership); } else { Membership membership = new Membership(userEntity.getId(), reference.getId(), reference.getType()); - membership.setRoles(Collections.singletonMap(role.getScope().getId(), role.getName())); + membership.setRoles(singletonMap(role.getScope().getId(), role.getName())); membership.setCreatedAt(updateDate); membership.setUpdatedAt(updateDate); returnedMembership = membershipRepository.create(membership); createAuditLog(MEMBERSHIP_CREATED, membership.getCreatedAt(), null, membership); - if (userEntity.getEmail() != null && !userEntity.getEmail().isEmpty()) { EmailNotification emailNotification = buildEmailNotification(userEntity, reference.getType(), reference.getId()); if (emailNotification != null) { emailService.sendAsyncEmailNotification(emailNotification); } } + if (GROUP.equals(reference.getType())) { + notifierService.trigger(GROUP_INVITATION, singletonMap("group", groupService.findById(reference.getId()))); + } } return convert(returnedMembership, role.getScope()); @@ -529,6 +520,12 @@ public Metadata findUserMembershipMetadata(List memberships, Mem } } + @Override + public int getNumberOfMembers(final MembershipReferenceType referenceType, final String referenceId, final RoleScope roleScope) { + return getMembers(referenceType, referenceId, roleScope).size() + + invitationService.findByReference(InvitationReferenceType.valueOf(referenceType.name()), referenceId).size(); + } + private Map getMemberPermissions(MembershipReferenceType membershipReferenceType, String referenceId, String userId, Set groups, RoleScope roleScope) { MemberEntity member = this.getMember(membershipReferenceType, referenceId, userId, roleScope); if (member != null) { diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/RoleServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/RoleServiceImpl.java index 42823c5bb3..c6acf7babd 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/RoleServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/RoleServiceImpl.java @@ -33,17 +33,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.awt.*; import java.util.*; -import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import static io.gravitee.management.model.permissions.RolePermissionAction.*; import static io.gravitee.repository.management.model.Audit.AuditProperties.ROLE; -import static io.gravitee.repository.management.model.Role.AuditEvent.ROLE_CREATED; -import static io.gravitee.repository.management.model.Role.AuditEvent.ROLE_DELETED; -import static io.gravitee.repository.management.model.Role.AuditEvent.ROLE_UPDATED; +import static io.gravitee.repository.management.model.Role.AuditEvent.*; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; /** * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com) @@ -85,7 +82,7 @@ public List findAll() { LOGGER.debug("Find all Roles"); return roleRepository.findAll() .stream() - .map(this::convert).collect(Collectors.toList()); + .map(this::convert).collect(toList()); } catch (TechnicalException ex) { LOGGER.error("An error occurs while trying to find all roles", ex); throw new TechnicalManagementException("An error occurs while trying to find all roles", ex); @@ -240,9 +237,10 @@ public void delete(final RoleScope scope, final String name) { public List findByScope(RoleScope scope) { try { LOGGER.debug("Find Roles by scope"); - return roleRepository.findByScope(scope) - .stream() - .map(this::convert).collect(Collectors.toList()); + return roleRepository.findByScope(scope).stream() + .map(this::convert) + .sorted(comparing(RoleEntity::getName)) + .collect(toList()); } catch (TechnicalException ex) { LOGGER.error("An error occurs while trying to find roles by scope", ex); throw new TechnicalManagementException("An error occurs while trying to find roles by scope", ex); @@ -260,7 +258,7 @@ public List findDefaultRoleByScopes(RoleScope... scopes) { stream(). filter(Role::isDefaultRole). map(this::convert). - collect(Collectors.toList()) + collect(toList()) ); } return roles; @@ -294,7 +292,7 @@ private void toggleDefaultRole(RoleScope scope, String newDefaultRoleName) throw List roles = roleRepository.findByScope(scope). stream(). filter(Role::isDefaultRole). - collect(Collectors.toList()); + collect(toList()); for (Role role : roles) { if(!role.getName().equals(newDefaultRoleName)) { Role previousRole = new Role(role); diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/UserServiceImpl.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/UserServiceImpl.java index f1b1a51a82..ff646a5201 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/UserServiceImpl.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/impl/UserServiceImpl.java @@ -24,6 +24,7 @@ import io.gravitee.management.model.parameters.Key; import io.gravitee.management.service.*; import io.gravitee.management.service.builder.EmailNotificationBuilder; +import io.gravitee.management.service.common.JWTHelper.ACTION; import io.gravitee.management.service.common.JWTHelper.Claims; import io.gravitee.management.service.exceptions.*; import io.gravitee.management.service.impl.search.SearchResult; @@ -58,9 +59,13 @@ import java.util.*; import java.util.stream.Collectors; +import static io.gravitee.management.service.common.JWTHelper.ACTION.*; import static io.gravitee.management.service.common.JWTHelper.DefaultValues.DEFAULT_JWT_EMAIL_REGISTRATION_EXPIRE_AFTER; import static io.gravitee.management.service.common.JWTHelper.DefaultValues.DEFAULT_JWT_ISSUER; +import static io.gravitee.management.service.notification.NotificationParamsBuilder.REGISTRATION_PATH; +import static io.gravitee.management.service.notification.NotificationParamsBuilder.RESET_PASSWORD_PATH; import static io.gravitee.repository.management.model.Audit.AuditProperties.USER; +import static java.util.stream.Collectors.toList; /** * @author David BRASSELY (david.brassely at graviteesource.com) @@ -73,50 +78,38 @@ public class UserServiceImpl extends AbstractService implements UserService { private final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class); - /** - * A default source used for user registration. - */ + /** A default source used for user registration.*/ private final static String IDP_SOURCE_GRAVITEE = "gravitee"; - @Autowired private UserRepository userRepository; - @Autowired private ConfigurableEnvironment environment; - @Autowired private EmailService emailService; - @Autowired private ApplicationService applicationService; - @Autowired private RoleService roleService; - @Autowired private MembershipService membershipService; - @Autowired private AuditService auditService; - @Autowired private NotifierService notifierService; - @Autowired private ApiService apiService; - @Autowired private ParameterService parameterService; + @Autowired + private SearchEngineService searchEngineService; + @Autowired + private InvitationService invitationService; @Value("${user.avatar:${gravitee.home}/assets/default_user_avatar.png}") private String defaultAvatar; - @Value("${user.login.defaultApplication:true}") private boolean defaultApplicationForFirstConnection; - @Autowired - private SearchEngineService searchEngineService; - private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Override @@ -257,22 +250,59 @@ private void checkUserRegistrationEnabled() { */ @Override public UserEntity create(final RegisterUserEntity registerUserEntity) { - checkUserRegistrationEnabled(); try { final String jwtSecret = environment.getProperty("jwt.secret"); if (jwtSecret == null || jwtSecret.isEmpty()) { throw new IllegalStateException("JWT secret is mandatory"); } - final Map claims = new JWTVerifier(jwtSecret).verify(registerUserEntity.getToken()); - String username = claims.get(Claims.SUBJECT).toString(); - - LOGGER.debug("Create an internal user {}", username); - Optional checkUser = userRepository.findById(username); - User user = checkUser.orElseThrow(() -> new UserNotFoundException(username)); + final String action = claims.get(Claims.ACTION).toString(); + if (USER_REGISTRATION.name().equals(action)) { + checkUserRegistrationEnabled(); + } else if (GROUP_INVITATION.name().equals(action)) { + // check invitations + final String email = claims.get(Claims.EMAIL).toString(); + final List invitations = invitationService.findAll(); + final List userInvitations = invitations.stream() + .filter(invitation -> invitation.getEmail().equals(email)) + .collect(toList()); + if (userInvitations.isEmpty()) { + throw new IllegalStateException("Invitation has been canceled"); + } + } + final Object subject = claims.get(Claims.SUBJECT); + User user; + if (subject == null) { + final NewExternalUserEntity externalUser = new NewExternalUserEntity(); + final String email = claims.get(Claims.EMAIL).toString(); + externalUser.setSource(IDP_SOURCE_GRAVITEE); + externalUser.setSourceId(email); + externalUser.setFirstname(registerUserEntity.getFirstname()); + externalUser.setLastname(registerUserEntity.getLastname()); + externalUser.setEmail(email); + user = convert(create(externalUser, true)); + } else { + final String username = subject.toString(); + LOGGER.debug("Create an internal user {}", username); + Optional checkUser = userRepository.findById(username); + user = checkUser.orElseThrow(() -> new UserNotFoundException(username)); + if (StringUtils.isNotBlank(user.getPassword())) { + throw new UserAlreadyExistsException(IDP_SOURCE_GRAVITEE, username); + } + } - if (StringUtils.isNotBlank(user.getPassword())) { - throw new UserAlreadyExistsException(IDP_SOURCE_GRAVITEE, username); + if (GROUP_INVITATION.name().equals(action)) { + // check invitations + final String email = user.getEmail(); + final String userId = user.getId(); + final List invitations = invitationService.findAll(); + invitations.stream() + .filter(invitation -> invitation.getEmail().equals(email)) + .forEach(invitation -> { + invitationService.addMember(invitation.getReferenceType().name(), invitation.getReferenceId(), + userId, invitation.getApiRole(), invitation.getApplicationRole()); + invitationService.delete(invitation.getId(), invitation.getReferenceId()); + }); } // Set date fields @@ -432,7 +462,7 @@ public UserEntity register(final NewExternalUserEntity newExternalUserEntity) { newExternalUserEntity.setSourceId(newExternalUserEntity.getEmail()); final UserEntity userEntity = create(newExternalUserEntity, true); - final Map params = getTokenRegistrationParams(userEntity, "/#!/registration/confirm/"); + final Map params = getTokenRegistrationParams(userEntity, REGISTRATION_PATH, USER_REGISTRATION); notifierService.trigger(PortalHook.USER_REGISTERED, params); @@ -447,7 +477,9 @@ public UserEntity register(final NewExternalUserEntity newExternalUserEntity) { return userEntity; } - private Map getTokenRegistrationParams(final UserEntity userEntity, final String portalUri) { + @Override + public Map getTokenRegistrationParams(final UserEntity userEntity, final String portalUri, + final ACTION action) { // generate a JWT to store user's information and for security purpose final Map claims = new HashMap<>(); claims.put(Claims.ISSUER, environment.getProperty("jwt.issuer", DEFAULT_JWT_ISSUER)); @@ -456,6 +488,7 @@ private Map getTokenRegistrationParams(final UserEntity userEnti claims.put(Claims.EMAIL, userEntity.getEmail()); claims.put(Claims.FIRSTNAME, userEntity.getFirstname()); claims.put(Claims.LASTNAME, userEntity.getLastname()); + claims.put(Claims.ACTION, action); final JWTSigner.Options options = new JWTSigner.Options(); options.setExpirySeconds(environment.getProperty("user.creation.token.expire-after", @@ -560,7 +593,7 @@ public Page search(String query, Pageable pageable) { List entities = users.getContent() .stream() .map(u -> convert(u, false)) - .collect(Collectors.toList()); + .collect(toList()); return new Page<>(entities, users.getPageNumber() + 1, @@ -624,7 +657,7 @@ public void resetPassword(final String id) { userRepository.update(user); final Map params = getTokenRegistrationParams(convert(user, false), - "/#!/resetPassword/"); + RESET_PASSWORD_PATH, RESET_PASSWORD); notifierService.trigger(PortalHook.PASSWORD_RESET, params); @@ -648,22 +681,30 @@ public void resetPassword(final String id) { } } - public void setDefaultApplicationForFirstConnection(boolean defaultApplicationForFirstConnection) { - this.defaultApplicationForFirstConnection = defaultApplicationForFirstConnection; - } - - private static User convert(NewExternalUserEntity newExternalUserEntity) { + private User convert(NewExternalUserEntity newExternalUserEntity) { if (newExternalUserEntity == null) { return null; } User user = new User(); - user.setEmail(newExternalUserEntity.getEmail()); user.setFirstname(newExternalUserEntity.getFirstname()); user.setLastname(newExternalUserEntity.getLastname()); user.setSource(newExternalUserEntity.getSource()); user.setSourceId(newExternalUserEntity.getSourceId()); + return user; + } + private User convert(UserEntity userEntity) { + if (userEntity == null) { + return null; + } + User user = new User(); + user.setId(userEntity.getId()); + user.setEmail(userEntity.getEmail()); + user.setFirstname(userEntity.getFirstname()); + user.setLastname(userEntity.getLastname()); + user.setSource(userEntity.getSource()); + user.setSourceId(userEntity.getSourceId()); return user; } diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/NotificationParamsBuilder.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/NotificationParamsBuilder.java index 25a6fe7ec0..f6ae12ee01 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/NotificationParamsBuilder.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/NotificationParamsBuilder.java @@ -19,7 +19,6 @@ import io.gravitee.management.model.api.ApiEntity; import io.gravitee.repository.management.model.ApiKey; -import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -45,10 +44,12 @@ public class NotificationParamsBuilder { public static final String PARAM_REGISTRATION_URL = "registrationUrl"; public static final String PARAM_EXPIRATION_DATE = "expirationDate"; + public static final String REGISTRATION_PATH = "/#!/registration/confirm/"; + public static final String RESET_PASSWORD_PATH = "/#!/resetPassword/"; public Map build() { - return Collections.unmodifiableMap(params); + return params; } public NotificationParamsBuilder application(ApplicationEntity app) { diff --git a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/PortalHook.java b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/PortalHook.java index 023e876d7f..7cb4ce5e7b 100644 --- a/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/PortalHook.java +++ b/gravitee-management-api-service/src/main/java/io/gravitee/management/service/notification/PortalHook.java @@ -21,10 +21,11 @@ */ public enum PortalHook implements Hook { - USER_REGISTERED("User Registered", "Triggered when a User is registered for the first time.", "USER"), - USER_FIRST_LOGIN("First Login", "Triggered when a user log in for the first time.", "USER"), - PASSWORD_RESET("Password Reset", "Triggered when a password is reset.", "USER"), + USER_REGISTERED("User Registered", "Triggered when a User is registered for the first time", "USER"), + USER_FIRST_LOGIN("First Login", "Triggered when a user log in for the first time", "USER"), + PASSWORD_RESET("Password Reset", "Triggered when a password is reset", "USER"), NEW_SUPPORT_TICKET("New Support Ticket", "Triggered when a new support ticket is created", "SUPPORT"), + GROUP_INVITATION("Group invitation", "Triggered when a user is invited in a group", "GROUP"), MESSAGE(null, null, null, true); private String label; diff --git a/gravitee-management-api-service/src/test/java/io/gravitee/management/service/MembershipService_AddOrUpdateMemberTest.java b/gravitee-management-api-service/src/test/java/io/gravitee/management/service/MembershipService_AddOrUpdateMemberTest.java index f646af5bef..9ae3ee8bae 100644 --- a/gravitee-management-api-service/src/test/java/io/gravitee/management/service/MembershipService_AddOrUpdateMemberTest.java +++ b/gravitee-management-api-service/src/test/java/io/gravitee/management/service/MembershipService_AddOrUpdateMemberTest.java @@ -41,8 +41,6 @@ import static java.util.Optional.empty; import static java.util.Optional.of; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.*; /** @@ -59,24 +57,20 @@ public class MembershipService_AddOrUpdateMemberTest { @Mock private MembershipRepository membershipRepository; - @Mock private UserService userService; - @Mock private EmailService emailService; - @Mock private RoleService roleService; - @Mock private GroupService groupService; - @Mock private AuditService auditService; - @Mock private IdentityService identityService; + @Mock + private NotifierService notifierService; @Test public void shouldAddApiGroupMembership() throws Exception { diff --git a/gravitee-management-api-service/src/test/java/io/gravitee/management/service/UserServiceTest.java b/gravitee-management-api-service/src/test/java/io/gravitee/management/service/UserServiceTest.java index c9aad04861..ad727756f0 100644 --- a/gravitee-management-api-service/src/test/java/io/gravitee/management/service/UserServiceTest.java +++ b/gravitee-management-api-service/src/test/java/io/gravitee/management/service/UserServiceTest.java @@ -42,12 +42,14 @@ import java.util.*; +import static io.gravitee.management.service.common.JWTHelper.ACTION.USER_REGISTRATION; import static java.util.Optional.empty; import static java.util.Optional.of; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.*; +import static org.springframework.test.util.ReflectionTestUtils.setField; /** * @author Azize Elamrani (azize dot elamrani at gmail dot com) @@ -102,6 +104,8 @@ public class UserServiceTest { private ParameterService mockParameterService; @Mock private SearchEngineService searchEngineService; + @Mock + private InvitationService invitationService; @Test public void shouldFindByUsername() throws TechnicalException { @@ -222,7 +226,7 @@ public void shouldNotConnectBecauseNotExists() throws TechnicalException { @Test public void shouldCreateDefaultApplication() throws TechnicalException { - userService.setDefaultApplicationForFirstConnection(true); + setField(userService, "defaultApplicationForFirstConnection", true); when(user.getLastConnectionAt()).thenReturn(null); when(userRepository.findById(USER_NAME)).thenReturn(of(user)); @@ -233,7 +237,7 @@ public void shouldCreateDefaultApplication() throws TechnicalException { @Test public void shouldNotCreateDefaultApplicationBecauseDisabled() throws TechnicalException { - userService.setDefaultApplicationForFirstConnection(false); + setField(userService, "defaultApplicationForFirstConnection", false); when(user.getLastConnectionAt()).thenReturn(null); when(userRepository.findById(USER_NAME)).thenReturn(of(user)); @@ -252,11 +256,15 @@ public void shouldNotCreateDefaultApplicationBecauseAlreadyConnected() throws Te verify(applicationService, never()).create(any(), eq(USER_NAME)); } - @Test(expected = IllegalStateException.class) + @Test(expected = TechnicalManagementException.class) public void shouldNotCreateUserIfRegistrationIsDisabled() { when(mockParameterService.findAsBoolean(Key.PORTAL_USERCREATION_ENABLED)).thenReturn(Boolean.FALSE); + when(environment.getProperty("jwt.secret")).thenReturn(JWT_SECRET); - userService.create(new RegisterUserEntity()); + RegisterUserEntity userEntity = new RegisterUserEntity(); + userEntity.setToken(createJWT(System.currentTimeMillis()/1000 + 100)); + + userService.create(userEntity); } @Test(expected = TechnicalManagementException.class) @@ -353,6 +361,7 @@ private String createJWT(long expirationSeconds) { claims.put(JWTHelper.Claims.EMAIL, EMAIL); claims.put(JWTHelper.Claims.FIRSTNAME, FIRST_NAME); claims.put(JWTHelper.Claims.LASTNAME, LAST_NAME); + claims.put(JWTHelper.Claims.ACTION, USER_REGISTRATION); claims.put("exp", expirationSeconds); return new JWTSigner(JWT_SECRET).sign(claims); } diff --git a/gravitee-management-api-standalone/gravitee-management-api-standalone-distribution/src/main/resources/templates/groupInvitation.html b/gravitee-management-api-standalone/gravitee-management-api-standalone-distribution/src/main/resources/templates/groupInvitation.html new file mode 100644 index 0000000000..0ebffbebbc --- /dev/null +++ b/gravitee-management-api-standalone/gravitee-management-api-standalone-distribution/src/main/resources/templates/groupInvitation.html @@ -0,0 +1,12 @@ + + +
+ <#include "header.html" /> +
+
+

Hi,

+

You have been invited to join the group ${group.name}.

+

Click here to register.

+
+ + diff --git a/gravitee-management-api-standalone/gravitee-management-api-standalone-distribution/src/main/resources/templates/notifications/portal/PORTAL.GROUP_INVITATION.yml b/gravitee-management-api-standalone/gravitee-management-api-standalone-distribution/src/main/resources/templates/notifications/portal/PORTAL.GROUP_INVITATION.yml new file mode 100644 index 0000000000..643a03b8a9 --- /dev/null +++ b/gravitee-management-api-standalone/gravitee-management-api-standalone-distribution/src/main/resources/templates/notifications/portal/PORTAL.GROUP_INVITATION.yml @@ -0,0 +1,3 @@ +title: Group invitation +message: | + You have been invited in group "${group.name}". \ No newline at end of file