display group membership on the profile page (/me)

This commit is contained in:
Sebastian Sdorra
2019-01-17 15:40:11 +01:00
parent b2e1dcf0e9
commit 66357ca196
12 changed files with 392 additions and 292 deletions

View File

@@ -8,7 +8,6 @@ public class MapperModule extends AbstractModule {
@Override
protected void configure() {
bind(UserDtoToUserMapper.class).to(Mappers.getMapper(UserDtoToUserMapper.class).getClass());
bind(MeToUserDtoMapper.class).to(Mappers.getMapper(MeToUserDtoMapper.class).getClass());
bind(UserToUserDtoMapper.class).to(Mappers.getMapper(UserToUserDtoMapper.class).getClass());
bind(UserCollectionToDtoMapper.class);
@@ -46,6 +45,7 @@ public class MapperModule extends AbstractModule {
bind(MergeResultToDtoMapper.class).to(Mappers.getMapper(MergeResultToDtoMapper.class).getClass());
// no mapstruct required
bind(MeDtoFactory.class);
bind(UIPluginDtoMapper.class);
bind(UIPluginDtoCollectionMapper.class);

View File

@@ -0,0 +1,26 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
public class MeDto extends HalRepresentation {
private String name;
private String displayName;
private String mail;
private List<String> groups;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
}
}

View File

@@ -0,0 +1,81 @@
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableList;
import de.otto.edison.hal.Links;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import sonia.scm.group.GroupNames;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.user.UserPermissions;
import javax.inject.Inject;
import java.util.Collections;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
public class MeDtoFactory extends LinkAppenderMapper {
private final ResourceLinks resourceLinks;
private final UserManager userManager;
@Inject
public MeDtoFactory(ResourceLinks resourceLinks, UserManager userManager) {
this.resourceLinks = resourceLinks;
this.userManager = userManager;
}
public MeDto create() {
PrincipalCollection principals = getPrincipalCollection();
MeDto dto = new MeDto();
User user = principals.oneByType(User.class);
mapUserProperties(user, dto);
mapGroups(principals, dto);
appendLinks(user, dto);
return dto;
}
private void mapGroups(PrincipalCollection principals, MeDto dto) {
Iterable<String> groups = principals.oneByType(GroupNames.class);
if (groups == null) {
groups = Collections.emptySet();
}
dto.setGroups(ImmutableList.copyOf(groups));
}
private void mapUserProperties(User user, MeDto dto) {
dto.setName(user.getName());
dto.setDisplayName(user.getDisplayName());
dto.setMail(user.getMail());
}
private PrincipalCollection getPrincipalCollection() {
Subject subject = SecurityUtils.getSubject();
return subject.getPrincipals();
}
private void appendLinks(User user, MeDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
if (UserPermissions.delete(user).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
}
if (UserPermissions.modify(user).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.me().update(target.getName())));
}
if (userManager.isTypeDefault(user) && UserPermissions.changePassword(user).isPermitted()) {
linksBuilder.single(link("password", resourceLinks.me().passwordChange()));
}
appendLinks(new EdisonLinkAppender(linksBuilder), new Me(), user);
target.add(linksBuilder.build());
}
}

View File

@@ -3,14 +3,11 @@ package sonia.scm.api.v2.resources;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.PasswordService;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.inject.Named;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -28,20 +25,18 @@ import javax.ws.rs.core.UriInfo;
*/
@Path(MeResource.ME_PATH_V2)
public class MeResource {
public static final String ME_PATH_V2 = "v2/me/";
private final MeToUserDtoMapper meToUserDtoMapper;
static final String ME_PATH_V2 = "v2/me/";
private final IdResourceManagerAdapter<User, UserDto> adapter;
private final PasswordService passwordService;
private final MeDtoFactory meDtoFactory;
private final UserManager userManager;
private final PasswordService passwordService;
@Inject
public MeResource(MeToUserDtoMapper meToUserDtoMapper, UserManager manager, PasswordService passwordService) {
this.meToUserDtoMapper = meToUserDtoMapper;
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
public MeResource(MeDtoFactory meDtoFactory, UserManager userManager, PasswordService passwordService) {
this.meDtoFactory = meDtoFactory;
this.userManager = userManager;
this.passwordService = passwordService;
this.userManager = manager;
}
/**
@@ -49,17 +44,15 @@ public class MeResource {
*/
@GET
@Path("")
@Produces(VndMediaType.USER)
@TypeHint(UserDto.class)
@Produces(VndMediaType.ME)
@TypeHint(MeDto.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@Context Request request, @Context UriInfo uriInfo) {
String id = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
return adapter.get(id, meToUserDtoMapper::map);
return Response.ok(meDtoFactory.create()).build();
}
/**
@@ -75,7 +68,10 @@ public class MeResource {
@TypeHint(TypeHint.NO_CONTENT.class)
@Consumes(VndMediaType.PASSWORD_CHANGE)
public Response changePassword(@Valid PasswordChangeDto passwordChange) {
userManager.changePasswordForLoggedInUser(passwordService.encryptPassword(passwordChange.getOldPassword()), passwordService.encryptPassword(passwordChange.getNewPassword()));
userManager.changePasswordForLoggedInUser(
passwordService.encryptPassword(passwordChange.getOldPassword()),
passwordService.encryptPassword(passwordChange.getNewPassword())
);
return Response.noContent().build();
}
}

View File

@@ -1,45 +0,0 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Links;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.user.UserPermissions;
import javax.inject.Inject;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
@Mapper
public abstract class MeToUserDtoMapper extends UserToUserDtoMapper {
@Inject
private UserManager userManager;
@Inject
private ResourceLinks resourceLinks;
@Override
@AfterMapping
protected void appendLinks(User user, @MappingTarget UserDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
if (UserPermissions.delete(user).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
}
if (UserPermissions.modify(user).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.me().update(target.getName())));
}
if (userManager.isTypeDefault(user)) {
linksBuilder.single(link("password", resourceLinks.me().passwordChange()));
}
appendLinks(new EdisonLinkAppender(linksBuilder), new Me(), user);
target.add(linksBuilder.build());
}
}