This page will introduce more detail about Customer Search
in back-end and language details of kotlin and the use of micronaut framework.
Related technologies
Customer Search controller
You can see the code in backoffice-manager.
Almost all controller functions have @BankUserOnly
. It verifies whether you have permission.
This annotation is defined in https://github.com/SafiBank/SaFiMono/blob/main/common/iam-auth-lib/src/main/kotlin/ph/safibank/common/auth/bankuser/BankUserOnly.kt .
@Target(ElementType.METHOD, ElementType.TYPE) (1) @Retention(RetentionPolicy.RUNTIME) (2) @Inheritedannotation class BankUserOnly
(1) indicates only method and type can use this annotation.
(2) means annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
This class is really important. We can see the class implemented SecurityRule
. And SecurityRule
extends Ordered
.
BankUserOnlyAnnotationRule
is customize SecurityRule, you should override getOrder
function. Write custom auth check logic in check
function.
And another important argument is BankUserAuthentication
:
@BankUserOnly fun functionName(bankUser: BankUserAuthentication)
BankUserAuthentication
is defined in the code:
/** * Represents authenticated BankUser * * It returns [BANK_USER_ROLE] for all bank users, that can be used with * [io.micronaut.security.annotation.Secured] or checked with [BankUserOnly] * annotation. * * It can be injected in to handlers using [BankUserAuthenticationArgumentBinder]: * ``` * @Controller * class ExampleController { * @Get("/") * @AllowBankUser * fun bankUserEndpoint(bankUser BankUserAuthentication) { * ... * } * } * ``` * * @see [BankUserOnly] */ data class BankUserAuthentication( /** * ID of Bank User in Okta */ val uid: String, /** * Display name as defined in Okta mapping */ val displayName: String, ) : Authentication { override fun getName(): String = displayName override fun getRoles(): List<String> = listOf(BANK_USER_ROLE) override fun getAttributes(): Map<String, Any> = hashMapOf() }
So controller function parameter bankUser: BankUserAuthentication
is handled by BankUserAuthenticationArgumentBinder
.
And BankUserAuthenticationArgumentBinder
is defined in the code:
@Singletonclass BankUserAuthenticationArgumentBinder : AbstractPrincipalArgumentBinder<BankUserAuthentication>(BankUserAuthentication::class.java)
Let’s see AbstractPrincipalArgumentBinder
, which is defined in io.micronaut.security.authentication
.
/** * Binds the authentication object to a route argument. * * @param <A> the {@link Principal} type * @author Burt Beckwith * @since 3.2 */ public abstract class AbstractPrincipalArgumentBinder<A extends Principal> implements TypedRequestArgumentBinder<A> { private final Class<A> authenticationClass; private final Argument<A> argumentType; protected AbstractPrincipalArgumentBinder(Class<A> authenticationClass) { this.authenticationClass = authenticationClass; argumentType = Argument.of(authenticationClass); } @SuppressWarnings("unchecked") @Override public BindingResult<A> bind(ArgumentConversionContext<A> context, HttpRequest<?> source) { if (!source.getAttributes().contains(SecurityFilter.KEY)) { return BindingResult.UNSATISFIED; } final Optional<A> existing = source.getUserPrincipal(authenticationClass); return existing.isPresent() ? (() -> existing) : BindingResult.EMPTY; } @Override public Argument<A> argumentType() { return argumentType; } }
This class implements TypedRequestArgumentBinder
(A TypeArgumentBinder that binds from an HttpRequest).
So if you want to write custom argument binder to implement it.
The function will bind
return BankUserAuthentication
, which has been stored in request.
To see how source.getUserPrincipal
works.
// The user principal stored within the request. default @NonNull <T extends Principal> Optional<T> getUserPrincipal(Class<T> principalType) { return getAttribute(HttpAttributes.PRINCIPAL, principalType);}
public enum HttpAttributes implements CharSequence { // Attribute used to store the {@link java.security.Principal}. PRINCIPAL("micronaut.AUTHENTICATION"), ... }
public interface HttpRequest<B> extends HttpMessage<B> { ... @Override default HttpRequest<B> setAttribute(CharSequence name, Object value) { return (HttpRequest<B>) HttpMessage.super.setAttribute(name, value); } }
When http request is sent to backoffice-manager controller. BankUserAuthentication
can be set to request. Then bankUser: BankUserAuthentication
can be injected into controller the parameter of function.
Attachments:
image-20221104-102706.png (image/png)
image-20221104-102847.png (image/png)
image-20221104-103031.png (image/png)