Skip to content

Commit 5d771f9

Browse files
committed
MID-11049 implementation for otp, not working yet, no otp setup
1 parent 6208e06 commit 5d771f9

File tree

13 files changed

+322
-160
lines changed

13 files changed

+322
-160
lines changed

gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/login/module/PageOtpCode.html

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,5 @@
2424

2525
<div wicket:id="csrfField"/>
2626
</form>
27-
2827
</wicket:extend>
29-
3028
</html>

gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/login/module/PageOtpCode.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
import java.io.Serial;
1010

11-
import com.evolveum.midpoint.authentication.api.util.AuthenticationModuleNameConstants;
12-
11+
import org.apache.wicket.markup.html.form.NumberTextField;
1312
import org.apache.wicket.markup.html.form.TextField;
1413
import org.apache.wicket.model.IModel;
14+
import org.apache.wicket.validation.ValidatorAdapter;
1515
import org.springframework.security.core.Authentication;
1616
import org.springframework.security.core.context.SecurityContextHolder;
1717

@@ -20,6 +20,7 @@
2020
import com.evolveum.midpoint.authentication.api.config.MidpointAuthentication;
2121
import com.evolveum.midpoint.authentication.api.config.ModuleAuthentication;
2222
import com.evolveum.midpoint.authentication.api.util.AuthUtil;
23+
import com.evolveum.midpoint.authentication.api.util.AuthenticationModuleNameConstants;
2324
import com.evolveum.midpoint.web.component.form.MidpointForm;
2425
import com.evolveum.midpoint.web.component.util.EnableBehaviour;
2526

@@ -29,38 +30,43 @@
2930
},
3031
permitAll = true,
3132
loginPage = true,
32-
authModule = AuthenticationModuleNameConstants.OTP)
33-
//TODO ModuleAuthentication because it might be either credentials or ldap module authentication
33+
authModule = AuthenticationModuleNameConstants.OTP)
34+
@SuppressWarnings("unused")
3435
public class PageOtpCode extends PageAbstractAuthenticationModule<ModuleAuthentication> {
3536

3637
@Serial private static final long serialVersionUID = 1L;
3738

38-
private static final String ID_CODE = "code";
39+
private static final String DEFAULT_FORM_URL = "./spring_security_login";
40+
private static final String OTP_FORM_URL = "/otp_verify";
3941

40-
public PageOtpCode() {
41-
super(null);
42-
}
42+
private static final String ID_CODE = "code";
4343

4444
@Override
4545
protected void initModuleLayout(MidpointForm form) {
4646
TextField<String> code = new TextField<>(ID_CODE);
4747
code.add(new EnableBehaviour(() -> searchUser() != null));
48+
// todo range validator
4849
form.add(code);
4950
}
5051

5152
protected String getUrlProcessingLogin() {
5253
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
5354
if (!(authentication instanceof MidpointAuthentication mpa)) {
54-
return "./spring_security_login";
55+
return DEFAULT_FORM_URL;
5556
}
5657

5758
ModuleAuthentication moduleAuthentication = mpa.getProcessingModuleAuthentication();
5859
if (isModuleApplicable(moduleAuthentication)) {
5960
String prefix = moduleAuthentication.getPrefix();
60-
return AuthUtil.stripSlashes(prefix) + "/spring_security_login";
61+
return AuthUtil.stripSlashes(prefix) + OTP_FORM_URL;
6162
}
6263

63-
return "./spring_security_login";
64+
return DEFAULT_FORM_URL;
65+
}
66+
67+
@Override
68+
protected boolean isModuleApplicable(ModuleAuthentication moduleAuthentication) {
69+
return AuthenticationModuleNameConstants.OTP.equals(moduleAuthentication.getModuleTypeName());
6470
}
6571

6672
@Override
@@ -77,14 +83,14 @@ protected IModel<String> getDefaultLoginPanelDescriptionModel() {
7783

7884
private boolean severalLoginFormModulesExist() {
7985
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
80-
if (authentication instanceof MidpointAuthentication mpAuthentication) {
81-
int loginFormModulesCount = (int) mpAuthentication.getAuthModules()
82-
.stream()
83-
.filter(module -> module != null && isModuleApplicable(module.getBaseModuleAuthentication()))
84-
.count();
85-
return loginFormModulesCount > 1;
86+
if (!(authentication instanceof MidpointAuthentication ma)) {
87+
return false;
8688
}
87-
return false;
89+
90+
int loginFormModulesCount = (int) ma.getAuthModules().stream()
91+
.filter(module -> module != null && isModuleApplicable(module.getBaseModuleAuthentication()))
92+
.count();
93+
return loginFormModulesCount > 1;
8894
}
8995

9096
private String getProcessingModuleName() {

infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,20 +2460,20 @@
24602460
</xsd:annotation>
24612461
</xsd:element>
24622462
<xsd:element name="attributeVerification" type="tns:AttributeVerificationCredentialsType" minOccurs="0" />
2463-
<xsd:element name="otp" type="tns:OtpCredentialsType" minOccurs="0" maxOccurs="unbounded"/>
2463+
<xsd:element name="otps" type="tns:OtpCredentialsType" minOccurs="0"/>
24642464
<!-- More credential types may be here, such as OTP seeds, X.509 credentials, etc. -->
24652465
</xsd:sequence>
24662466
<xsd:attribute name="id" type="xsd:long"/>
24672467
</xsd:complexType>
24682468
<xsd:element name="credentials" type="tns:CredentialsType"/>
24692469

2470-
<xsd:complexType name="OtpCredentialsType">
2470+
<xsd:complexType name="OtpCredentialType">
24712471
<xsd:annotation>
24722472
<xsd:documentation>
24732473
<!-- todo -->
24742474
</xsd:documentation>
24752475
<xsd:appinfo>
2476-
<a:since>4.10</a:since>
2476+
<a:since>4.11</a:since>
24772477
<a:container/>
24782478
</xsd:appinfo>
24792479
</xsd:annotation>
@@ -2508,6 +2508,25 @@
25082508
</xsd:complexContent>
25092509
</xsd:complexType>
25102510

2511+
<xsd:complexType name="OtpCredentialsType">
2512+
<xsd:annotation>
2513+
<xsd:documentation>
2514+
<!-- todo -->
2515+
</xsd:documentation>
2516+
<xsd:appinfo>
2517+
<a:since>4.11</a:since>
2518+
<a:container/>
2519+
</xsd:appinfo>
2520+
</xsd:annotation>
2521+
<xsd:complexContent>
2522+
<xsd:extension base="tns:AbstractCredentialType">
2523+
<xsd:sequence>
2524+
<xsd:element name="otp" type="tns:OtpCredentialType" minOccurs="0" maxOccurs="unbounded"/>
2525+
</xsd:sequence>
2526+
</xsd:extension>
2527+
</xsd:complexContent>
2528+
</xsd:complexType>
2529+
25112530
<xsd:complexType name="BehaviorType">
25122531
<xsd:annotation>
25132532
<xsd:documentation>

infra/schema/src/main/resources/xml/ns/public/common/common-security-3.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,6 +2373,7 @@
23732373
</xsd:annotation>
23742374
</xsd:element>
23752375
<xsd:element name="attributeVerification" type="tns:AttributeVerificationCredentialsPolicyType" minOccurs="0"/>
2376+
<xsd:element name="otp" type="tns:OtpCredentialsPolicyType" minOccurs="0"/>
23762377
<!-- More credential types may come here in the future. -->
23772378
</xsd:sequence>
23782379
<xsd:attribute name="id" type="xsd:long"/>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2010-2026 Evolveum and contributors
3+
*
4+
* Licensed under the EUPL-1.2 or later.
5+
*/
6+
7+
package com.evolveum.midpoint.authentication.impl.otp;
8+
9+
import java.util.List;
10+
11+
import com.evolveum.midpoint.authentication.api.AuthenticationChannel;
12+
import com.evolveum.midpoint.authentication.api.evaluator.context.AbstractAuthenticationContext;
13+
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
14+
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
15+
import com.evolveum.midpoint.xml.ns._public.common.common_3.OtpCredentialsPolicyType;
16+
17+
public class OtpAuthenticationContext extends AbstractAuthenticationContext {
18+
19+
private final Integer code;
20+
21+
private OtpCredentialsPolicyType policy;
22+
23+
public OtpAuthenticationContext(
24+
String username,
25+
Class<? extends FocusType> principalType,
26+
Integer code,
27+
List<ObjectReferenceType> requireAssignment,
28+
AuthenticationChannel channel) {
29+
30+
super(username, principalType, requireAssignment, channel);
31+
32+
this.code = code;
33+
}
34+
35+
@Override
36+
public Integer getEnteredCredential() {
37+
return getCode();
38+
}
39+
40+
public Integer getCode() {
41+
return code;
42+
}
43+
44+
public OtpCredentialsPolicyType getPolicy() {
45+
return policy;
46+
}
47+
48+
public void setPolicy(OtpCredentialsPolicyType policy) {
49+
this.policy = policy;
50+
}
51+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright (c) 2010-2026 Evolveum and contributors
3+
*
4+
* Licensed under the EUPL-1.2 or later.
5+
*/
6+
7+
package com.evolveum.midpoint.authentication.impl.otp;
8+
9+
import java.util.List;
10+
11+
import org.jetbrains.annotations.NotNull;
12+
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
13+
import org.springframework.security.authentication.BadCredentialsException;
14+
import org.springframework.security.core.context.SecurityContextHolder;
15+
import org.springframework.stereotype.Component;
16+
17+
import com.evolveum.midpoint.authentication.api.util.AuthUtil;
18+
import com.evolveum.midpoint.authentication.impl.evaluator.CredentialsAuthenticationEvaluatorImpl;
19+
import com.evolveum.midpoint.security.api.ConnectionEnvironment;
20+
import com.evolveum.midpoint.security.api.MidPointPrincipal;
21+
import com.evolveum.midpoint.security.api.SecurityUtil;
22+
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
23+
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
24+
25+
@Component
26+
public class OtpAuthenticationEvaluator
27+
extends CredentialsAuthenticationEvaluatorImpl<OtpCredentialsType, OtpAuthenticationContext> {
28+
29+
@Override
30+
protected void checkEnteredCredentials(ConnectionEnvironment env, OtpAuthenticationContext ctx) {
31+
if (ctx.getEnteredCredential() == null) {
32+
recordModuleAuthenticationFailure(ctx.getUsername(), null, env, null, "empty otp code provided");
33+
throw new BadCredentialsException(AuthUtil.generateBadCredentialsMessageKey(SecurityContextHolder.getContext().getAuthentication()));
34+
}
35+
}
36+
37+
@Override
38+
protected boolean supportsAuthzCheck() {
39+
return true;
40+
}
41+
42+
@Override
43+
protected OtpCredentialsType getCredential(CredentialsType credentials) {
44+
return credentials.getOtps();
45+
}
46+
47+
@Override
48+
protected void validateCredentialNotNull(ConnectionEnvironment connEnv, @NotNull MidPointPrincipal principal, OtpCredentialsType credentials) {
49+
List<OtpCredentialType> securityQuestionsAnswers = credentials.getOtp();
50+
51+
if (securityQuestionsAnswers == null || securityQuestionsAnswers.isEmpty()) {
52+
recordModuleAuthenticationFailure(principal.getUsername(), principal, connEnv, null, "no stored security questions");
53+
throw new AuthenticationCredentialsNotFoundException("web.security.provider.securityQuestion.bad");
54+
}
55+
}
56+
57+
@Override
58+
protected boolean passwordMatches(
59+
ConnectionEnvironment env,
60+
@NotNull MidPointPrincipal principal,
61+
OtpCredentialsType otpCredentials,
62+
OtpAuthenticationContext ctx) {
63+
64+
if (otpCredentials == null) {
65+
return false;
66+
}
67+
68+
final Integer code = ctx.getEnteredCredential();
69+
if (code == null) {
70+
return false;
71+
}
72+
73+
OtpService service; // todo initialize service
74+
75+
for (OtpCredentialType otp : otpCredentials.getOtp()) {
76+
ProtectedStringType secret = otp.getSecret();
77+
if (secret == null) {
78+
return false;
79+
}
80+
81+
// service.verifyCode(secret, )
82+
}
83+
84+
return false;
85+
}
86+
87+
@Override
88+
protected CredentialPolicyType getEffectiveCredentialPolicy(SecurityPolicyType securityPolicy, OtpAuthenticationContext ctx) {
89+
OtpCredentialsPolicyType policy = ctx.getPolicy();
90+
if (policy == null) {
91+
policy = SecurityUtil.getEffectiveOtpCredentialsPolicy(securityPolicy);
92+
}
93+
ctx.setPolicy(policy);
94+
return policy;
95+
}
96+
97+
@Override
98+
protected boolean supportsActivation() {
99+
return true;
100+
}
101+
}

0 commit comments

Comments
 (0)