ProfileManagerScim.java
package jasper.component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.unboundid.scim2.common.types.Email;
import io.micrometer.core.annotation.Timed;
import jasper.client.ScimClient;
import jasper.component.dto.ScimPatchOp;
import jasper.component.dto.ScimUserResource;
import jasper.config.Props;
import jasper.security.Auth;
import jasper.service.dto.ProfileDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import static jasper.security.AuthoritiesConstants.PRIVATE;
@Profile("scim")
@Component
public class ProfileManagerScim implements ProfileManager {
private static final Logger logger = LoggerFactory.getLogger(ProfileManagerScim.class);
@Autowired
Props props;
@Autowired
Auth auth;
@Autowired
ScimClient scimClient;
@Autowired
AccessToken accessToken;
@Autowired
ObjectMapper objectMapper;
@Override
@Timed(value = "jasper.scim", histogram = true)
public void createUser(String userName, String password, String[] roles) {
var user = ScimUserResource.builder()
.userName(userName)
.password(password)
.customClaims(getClaims(roles))
.emails(List.of(new Email().setValue(userName + "@jasper.local")))
.build();
scimClient.createUser(baseUri(), accessToken.getAdminToken(), user);
}
private URI baseUri() {
try {
return new URI(auth.security().getScimEndpoint());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private ObjectNode getClaims(String[] roles) {
var claims = objectMapper.createObjectNode();
return claims.set(auth.security().getAuthoritiesClaim(), new TextNode(String.join(",", roles)));
}
private String[] getRoles(ScimUserResource user) {
if (user.getCustomClaims() != null &&
user.getCustomClaims().get(auth.security().getAuthoritiesClaim()) != null) {
return user.getCustomClaims().get(auth.security().getAuthoritiesClaim()).asText().split(",");
}
return new String[] { props.getDefaultRole(), auth.security().getDefaultRole() };
}
@Override
@Timed(value = "jasper.scim", histogram = true)
public ProfileDto getUser(String userName) {
return mapUser(_getUser(userName));
}
private ProfileDto mapUser(ScimUserResource user) {
var result = new ProfileDto();
result.setActive(user.isActive());
var roles = Arrays.asList(getRoles(user));
for (var role : roles) {
if (!role.equals(PRIVATE)) {
result.setRole(role);
break;
}
}
if (roles.contains(PRIVATE)) {
result.setTag("_user/" + user.getUserName());
} else {
result.setTag("+user/" + user.getUserName());
}
return result;
}
private ScimUserResource _getUser(String userName) {
return objectMapper.convertValue(
scimClient.getUser(baseUri(), accessToken.getAdminToken(), userName).getResources().get(0),
ScimUserResource.class);
}
@Override
@Timed(value = "jasper.scim", histogram = true)
public Page<ProfileDto> getUsers(String origin, int page, int size) {
var users = scimClient.getUsers(baseUri(), accessToken.getAdminToken(), page + 1, size);
return new PageImpl<>(
users.getResources(),
PageRequest.of(users.getStartIndex() - 1, users.getItemsPerPage()),
users.getTotalResults())
.map(m -> objectMapper.convertValue(m, ScimUserResource.class))
.map(u -> mapUser(u));
}
@Override
@Timed(value = "jasper.scim", histogram = true)
public void changePassword(String userName, String password) {
var id = (String) scimClient
.getUser(baseUri(), accessToken.getAdminToken(), userName).getResources()
.get(0)
.get("id");
var patch = ScimPatchOp.builder().operations(List.of(
ScimPatchOp.Operation.builder()
.op("replace")
.path("password")
.value(password)
.build()
)).build();
scimClient.patchUser(baseUri(), accessToken.getAdminToken(), id, patch);
}
@Override
@Timed(value = "jasper.scim", histogram = true)
public void setActive(String userName, boolean active) {
var user = _getUser(userName);
var patch = ScimPatchOp.builder().operations(List.of(
ScimPatchOp.Operation.builder()
.op("replace")
.path("active")
.value(active)
.build()
)).build();
scimClient.patchUser(baseUri(), accessToken.getAdminToken(), user.getId(), patch);
}
@Override
@Timed(value = "jasper.scim", histogram = true)
public void changeRoles(String userName, String[] roles) {
var user = _getUser(userName);
var claims = user.getCustomClaims();
if (claims == null || claims.isNull()) {
claims = getClaims(roles);
} else {
claims.set(auth.security().getAuthoritiesClaim(), new TextNode(String.join(",", roles)));
}
var patch = ScimPatchOp.builder().operations(List.of(
ScimPatchOp.Operation.builder()
.op("replace")
.path("customClaims")
.value(claims)
.build()
)).build();
scimClient.patchUser(baseUri(), accessToken.getAdminToken(), user.getId(), patch);
}
@Override
@Timed(value = "jasper.scim", histogram = true)
public void deleteUser(String userName) {
var user = _getUser(userName);
scimClient.deleteUser(baseUri(), accessToken.getAdminToken(), user.getId());
}
}