Plug-In Development
Custom user client
We implement basic user management clients for public and keycloak user management systems. But you can also add your own client. You only need to add three files to the repository. Replace "yourClient" and "yourVersion" in the code snippets.
1) Create a new package
com.codamai.cms.authentication.clients.%yourName%.%yourVersion%
2) Add a new Configuration File
YourClientConfiguration.java
@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "cms_user_management_client", havingValue = "yourClient.yourVersion")
public class YourClientConfiguration {
// add your individual configuration
// example...
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).anonymous()
.antMatchers("/swagger-ui/**", "/swagger-resources/**", "/v2/api-docs", "/actuator/**").anonymous().anyRequest()
.authenticated().and().oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
// ...example end
}
3) Add a new Sever Setup File
YourClientServerSetup.java
@Log4j2
@Component
@ConditionalOnProperty(name = "cms_user_management_client", havingValue = "yourClient.yourVersion")
public class KeycloakServerSetup implements AuthenticationServerInterface {
/** Some variables for your setup. */
@Value("${yourClient_yourData}")
private String YOUR_DATA;
@Override
public void setup() {
// setup of roles
}
/**
* Initialize all required roles in Authentification-Service.
*
* @param modelClass Model to set Roles for.
*/
public void initRolesForModel(Class<? extends AbstractModel> modelClass) {
String modelName = modelClass.getSimpleName().toLowerCase();
// Basic Roles CRUDL
RolesResource allRealmRoles = KeycloakUserClient.client.roles();
String operationModelRoleId = modelName;
// Simple Role for Model with ModelClass Name
if (!roleExists(operationModelRoleId, allRealmRoles)) {
createRole(operationModelRoleId);
}
// Need Create Role for Model
if (modelClass.isAnnotationPresent(ClassAccessValidatorCreate.class)) {
String[] roleIds = modelClass.getAnnotation(ClassAccessValidatorCreate.class).roles();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Need Read Role for Model
if (modelClass.isAnnotationPresent(ClassAccessValidatorRead.class)) {
String[] roleIds = modelClass.getAnnotation(ClassAccessValidatorRead.class).roles();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Need Update Role for Model
if (modelClass.isAnnotationPresent(ClassAccessValidatorUpdate.class)) {
String[] roleIds = modelClass.getAnnotation(ClassAccessValidatorUpdate.class).roles();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Need Delete Role for Model
if (modelClass.isAnnotationPresent(ClassAccessValidatorDelete.class)) {
String[] roleIds = modelClass.getAnnotation(ClassAccessValidatorDelete.class).roles();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Special Create for Fields?
for (Field field : modelClass.getDeclaredFields()) {
// Need Read Role for Field (recursive create)
if (field.isAnnotationPresent(FieldAccessValidatorCreate.class)) {
String[] roleIds = field.getAnnotation(FieldAccessValidatorCreate.class).role();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Need Read Role for Field (recursive read)
if (field.isAnnotationPresent(FieldAccessValidatorRead.class)) {
String[] roleIds = field.getAnnotation(FieldAccessValidatorRead.class).role();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Need Update Role for Field (recursive update)
if (field.isAnnotationPresent(FieldAccessValidatorUpdate.class)) {
String[] roleIds = field.getAnnotation(FieldAccessValidatorUpdate.class).role();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
// Need Delete Role for Field (recursive delete)
if (field.isAnnotationPresent(FieldAccessValidatorRead.class)) {
String[] roleIds = field.getAnnotation(FieldAccessValidatorRead.class).role();
for (String roleId : roleIds) {
if (!roleExists(roleId, allRealmRoles)) {
createRole(roleId);
}
}
}
}
}
@SuppressWarnings("static-method")
private void createRole(String pRoleName) {
RoleRepresentation keycloakRole = new RoleRepresentation();
keycloakRole.setName(pRoleName);
KeycloakUserClient.client.roles().create(keycloakRole);
log.warn("Role {} successfully created in Authentication-Service.", pRoleName);
}
}
4) Add a new User Setup File
YourClientUserClient.java
@Log4j2
@Component
@ConditionalOnProperty(name = "cms_user_management_client", havingValue = "yourClient.yourVersion")
public class KeycloakUserClient implements UserClientInterface {
/** example system database. */
@Autowired
private SystemDatabaseController database;
@Override
public User getCurrentUser() throws NoUserLoggedInException {
SecurityContext context = SecurityContextHolder.getContext();
if (context.getAuthentication() == null || context.getAuthentication().getPrincipal() == null) {
throw new NoUserLoggedInException();
}
}
@Override
public String[] getAllRolesByRealm() {}
@Override
public String[] getAllRolesByCmsClient() {}
@Override
public void createRole(String pRoleName) throws RoleExistException {}
}
Start the CMS with your own client
Custom configuration
Environment | Description | Example |
---|---|---|
user_management_client | Your own implementation of a user management. | yourClient.yourVersion |
Extend rest api
It is very easy to extend your CMS REST API, just add a basic Spring Boot Rest API file in
com.codamai.cms.api.rest.custom.yourVersion
Example: YourRestApi.java
@Log4j2
@RestController
@RequestMapping(
path = "v2/YourRestApi/",
consumes = "application/json",
produces = "application/json"
)
public class YourRestApi {
/** the database controller for current T. */
@Autowired
private AbstractDatabaseController<T> database;
/** the access controller for current T. */
@Autowired
private AccessValidator<T> access;
/** create data. */
@Autowired
private AbstractCreateModelController<T> create;
/** read data. */
@Autowired
private AbstractReadModelController<T> read;
/** patch data. */
@Autowired
private AbstractPatchModelController<T> patch;
/** update data. */
@Autowired
private AbstractUpdateModelController<T> update;
/** delete data. */
@Autowired
private AbstractDeleteModelController<T> delete;
@Operation(summary = "do something.", description = "do something.")
@PostMapping(path = "doSomething")
public YourResponseObject create(@RequestBody YourPayloadObject payload) {
// do something with controller or database...
}
}