">
Plug-In Development | CodamAI - SaaS architecture and code assistant

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

EnvironmentDescriptionExample
user_management_clientYour 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...
    }

}
Last Updated:
Contributors: mertins-d, Daniel mertins