Skip to content

Commit

Permalink
login recovery implementation - archetype selection module (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
katkav committed Jul 12, 2023
1 parent 1cc07bc commit 2a8e5fe
Show file tree
Hide file tree
Showing 21 changed files with 371 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ private String getLoginRecoveryUrl(SecurityPolicyType securityPolicy) {
if (loginRecoveryPolicy == null) {
return "";
}
return SecurityUtils.getChannelUrlSuffixFromAuthSequence(
loginRecoveryPolicy.getAuthenticationSequenceIdentifier(), securityPolicy);
return getAuthLinkUrl(loginRecoveryPolicy.getAuthenticationSequenceIdentifier(), securityPolicy);
}

private void addForgotPasswordLink(SecurityPolicyType securityPolicy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.gui.impl.page.lostusername;
package com.evolveum.midpoint.gui.impl.page.login;

import com.evolveum.midpoint.authentication.api.authorization.PageDescriptor;
import com.evolveum.midpoint.authentication.api.authorization.Url;
import com.evolveum.midpoint.authentication.api.config.MidpointAuthentication;
import com.evolveum.midpoint.authentication.api.config.ModuleAuthentication;
import com.evolveum.midpoint.authentication.api.util.AuthenticationModuleNameConstants;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.util.GuiDisplayTypeUtil;
import com.evolveum.midpoint.gui.api.util.LocalizationUtil;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.gui.impl.component.tile.Tile;
import com.evolveum.midpoint.gui.impl.component.tile.TilePanel;
import com.evolveum.midpoint.gui.impl.page.login.AbstractPageLogin;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.Producer;
Expand All @@ -25,22 +28,31 @@
import com.evolveum.midpoint.web.component.AjaxButton;
import com.evolveum.midpoint.web.component.form.MidpointForm;

import com.evolveum.midpoint.web.component.prism.DynamicFormPanel;
import com.evolveum.midpoint.web.page.error.PageError;
import com.evolveum.midpoint.web.security.util.SecurityUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@PageDescriptor(urls = { @Url(mountUrl = "/loginRecovery", matchUrlForSecurity = "/loginRecovery") }, permitAll = true)
public class PageLoginNameRecovery extends AbstractPageLogin {
@PageDescriptor(urls = {
@Url(mountUrl = "/archetypeSelection", matchUrlForSecurity = "/archetypeSelection")
}, permitAll = true, loginPage = true, authModule = AuthenticationModuleNameConstants.ARCHETYPE_SELECTION)
public class PageLoginNameRecovery extends PageAuthenticationBase {

private static final Trace LOGGER = TraceManager.getTrace(PageLoginNameRecovery.class);
private static final String DOT_CLASS = PageLoginNameRecovery.class.getName() + ".";
Expand All @@ -53,35 +65,92 @@ public class PageLoginNameRecovery extends AbstractPageLogin {
private static final String ID_ARCHETYPES_PANEL = "archetypes";
private static final String ID_ARCHETYPE_PANEL = "archetype";

private LoadableDetachableModel<ArchetypeBasedModuleType> archetypeBasedAuthModuleModel;
private LoadableDetachableModel<ArchetypeSelectionModuleType> archetypeBasedAuthModuleModel;

public PageLoginNameRecovery() {
super();
}

@Override
protected ObjectQuery createStaticFormQuery() {
String username = "";
return getPrismContext().queryFor(UserType.class).item(UserType.F_NAME)
.eqPoly(username).matchingNorm().build();
}

@Override
protected DynamicFormPanel<UserType> getDynamicForm() {
return null;
}

@Override
protected void initModels() {
archetypeBasedAuthModuleModel = new LoadableDetachableModel<>() {
private static final long serialVersionUID = 1L;

@Override
protected ArchetypeBasedModuleType load() {
protected ArchetypeSelectionModuleType load() {
return loadArchetypeBasedModule();
}
};
}

private ArchetypeBasedModuleType loadArchetypeBasedModule() {
Task task = createAnonymousTask(OPERATION_LOAD_ARCHETYPE_BASED_MODULE);
OperationResult parentResult = new OperationResult(OPERATION_LOAD_ARCHETYPE_BASED_MODULE);
try {
var securityPolicy = getModelInteractionService().getSecurityPolicy((PrismObject<? extends FocusType>) null,
task, parentResult);
return SecurityUtils.getLoginRecoveryAuthModule(securityPolicy);
} catch (CommonException e) {
LOGGER.warn("Cannot load authentication module for login recovery: " + e.getMessage(), e);
private ArchetypeSelectionModuleType getModuleByIdentifier(String moduleIdentifier) {
if (StringUtils.isEmpty(moduleIdentifier)) {
return null;
}
return null;
//TODO user model?
SecurityPolicyType securityPolicy = resolveSecurityPolicy(null);
if (securityPolicy == null || securityPolicy.getAuthentication() == null) {
getSession().error(getString("Security policy not found"));
throw new RestartResponseException(PageError.class);
}
return securityPolicy.getAuthentication().getModules().getArchetypeSelection()
.stream()
.filter(m -> moduleIdentifier.equals(m.getIdentifier()) || moduleIdentifier.equals(m.getName()))
.findFirst()
.orElse(null);
}

private ArchetypeSelectionModuleType loadArchetypeBasedModule() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof MidpointAuthentication)) {
getSession().error(getString("No midPoint authentication is found"));
throw new RestartResponseException(PageError.class);
}
MidpointAuthentication mpAuthentication = (MidpointAuthentication) authentication;
ModuleAuthentication moduleAuthentication = mpAuthentication.getProcessingModuleAuthentication();
if (moduleAuthentication == null
&& !AuthenticationModuleNameConstants.ARCHETYPE_SELECTION.equals(moduleAuthentication.getModuleTypeName())) {
getSession().error(getString("No authentication module is found"));
throw new RestartResponseException(PageError.class);
}
if (StringUtils.isEmpty(moduleAuthentication.getModuleIdentifier())) {
getSession().error(getString("No module identifier is defined"));
throw new RestartResponseException(PageError.class);
}
ArchetypeSelectionModuleType module = getModuleByIdentifier(moduleAuthentication.getModuleIdentifier());
if (module == null) {
getSession().error(getString("No module with identifier \"" + moduleAuthentication.getModuleIdentifier() + "\" is found"));
throw new RestartResponseException(PageError.class);
}
return module;
// List<ModuleItemConfigurationType> itemConfigs = module.getItem();
// return itemConfigs.stream()
// .map(config -> config.getPath())
// .collect(Collectors.toList());
//
//
// Task task = createAnonymousTask(OPERATION_LOAD_ARCHETYPE_BASED_MODULE);
// OperationResult parentResult = new OperationResult(OPERATION_LOAD_ARCHETYPE_BASED_MODULE);
// try {
// var securityPolicy = getModelInteractionService().getSecurityPolicy((PrismObject<? extends FocusType>) null,
// task, parentResult);
// return SecurityUtils.getLoginRecoveryAuthModule(securityPolicy);
// } catch (CommonException e) {
// LOGGER.warn("Cannot load authentication module for login recovery: " + e.getMessage(), e);
// }
// return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public static String getChannelUrlSuffixFromAuthSequence(String sequenceIdentifi
return channel.getUrlSuffix();
}

public static ArchetypeBasedModuleType getLoginRecoveryAuthModule(SecurityPolicyType securityPolicy) {
public static ArchetypeSelectionModuleType getLoginRecoveryAuthModule(SecurityPolicyType securityPolicy) {
if (securityPolicy == null || securityPolicy.getAuthentication() == null
|| securityPolicy.getAuthentication().getModules() == null) {
return null;
Expand All @@ -233,8 +233,8 @@ public static ArchetypeBasedModuleType getLoginRecoveryAuthModule(SecurityPolicy
//for now only one archetype based module is supported for login recovery functionality
var loginRecoveryModule = modules.get(0);
var recoveryModuleIdentifier = loginRecoveryModule.getIdentifier();
List<ArchetypeBasedModuleType> archetypeBasedModules =
securityPolicy.getAuthentication().getModules().getArchetypeBased();
List<ArchetypeSelectionModuleType> archetypeBasedModules =
securityPolicy.getAuthentication().getModules().getArchetypeSelection();
return archetypeBasedModules
.stream()
.filter(m -> m.getIdentifier().equals(recoveryModuleIdentifier))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,6 @@ public abstract class SchemaConstants {
public static final QName CHANNEL_REMEDIATION_QNAME = new QName(NS_CHANNEL, "remediation");
public static final String CHANNEL_REMEDIATION_URI = qNameToUri(CHANNEL_REMEDIATION_QNAME);

public static final QName CHANNEL_LOGIN_RECOVERY_QNAME = new QName(NS_CHANNEL, "loginRecovery");
public static final String CHANNEL_LOGIN_RECOVERY_URI = qNameToUri(CHANNEL_LOGIN_RECOVERY_QNAME);
public static final String NS_MODEL_DISABLE_REASON = NS_MODEL + "/disableReason";
public static final String MODEL_DISABLE_REASON_EXPLICIT =
qNameToUri(new QName(NS_MODEL_DISABLE_REASON, "explicit"));
Expand Down Expand Up @@ -458,6 +456,11 @@ public abstract class SchemaConstants {
public static final QName CHANNEL_RESET_PASSWORD_QNAME = new QName(NS_CHANNEL, "resetPassword");
public static final String CHANNEL_RESET_PASSWORD_URI = qNameToUri(CHANNEL_RESET_PASSWORD_QNAME);


public static final QName CHANNEL_LOGIN_RECOVERY_QNAME = new QName(NS_CHANNEL, "loginRecovery");
public static final String CHANNEL_LOGIN_RECOVERY_URI = qNameToUri(CHANNEL_LOGIN_RECOVERY_QNAME);


// Catch-all channel for all user operations in user interface.
public static final String CHANNEL_USER_LOCAL = "user";
public static final QName CHANNEL_USER_QNAME = new QName(NS_CHANNEL, "user");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
<xsd:element name="focusIdentification" type="tns:FocusIdentificationAuthenticationModuleType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="hint" type="tns:HintAuthenticationModuleType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="other" type="tns:OtherAuthenticationModuleType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="archetypeBased" type="tns:ArchetypeBasedModuleType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="archetypeSelection" type="tns:ArchetypeSelectionModuleType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:long"/>
</xsd:complexType>
Expand Down Expand Up @@ -393,7 +393,7 @@
</xsd:complexContent>
</xsd:complexType>

<xsd:complexType name="ArchetypeBasedModuleType">
<xsd:complexType name="ArchetypeSelectionModuleType">
<xsd:annotation>
<xsd:documentation>
Module is used for the user authentication using the rules from the set of archetypes. For example, for "lost my
Expand All @@ -405,7 +405,7 @@
</xsd:appinfo>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="tns:AbstractAuthenticationModuleType">
<xsd:extension base="tns:AbstractCredentialAuthenticationModuleType">
<xsd:sequence>
<xsd:element name="archetypeSelection" type="tns:ArchetypeSelectionType" minOccurs="0">
<xsd:annotation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class AuthenticationModuleNameConstants {
public static final String OTHER = "Other";
public static final String ATTRIBUTE_VERIFICATION = "AttrVerification";
public static final String FOCUS_IDENTIFICATION = "FocusIdentification";
public static final String ARCHETYPE_SELECTION = "ArchetypeSelection";
public static final String CORRELATION_ATTRIBUTES_VERIFICATION = "CorrelationAttributesVerification";
public static final String HINT = "Hint";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.authentication.impl.channel;

import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.security.api.Authorization;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthenticationSequenceChannelType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationType;

import java.util.ArrayList;
import java.util.Collection;

public class LoginRecoveryAuthenticationChannel extends AuthenticationChannelImpl {

public LoginRecoveryAuthenticationChannel(AuthenticationSequenceChannelType channel) {
super(channel);
}

public String getChannelId() {
return SchemaConstants.CHANNEL_LOGIN_RECOVERY_URI;
}

public String getPathAfterSuccessfulAuthentication() {
return "/loginRecovery";
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2010-2019 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.authentication.impl.factory.channel;

import com.evolveum.midpoint.authentication.api.AuthenticationChannel;
import com.evolveum.midpoint.authentication.impl.channel.LoginRecoveryAuthenticationChannel;
import com.evolveum.midpoint.authentication.impl.channel.SelfRegistrationAuthenticationChannel;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthenticationSequenceChannelType;

import org.springframework.stereotype.Component;

@Component
public class LoginRecoveryChannelFactory extends AbstractChannelFactory {

@Override
public boolean match(String channelId) {
return SchemaConstants.CHANNEL_LOGIN_RECOVERY_URI.equals(channelId);
}

@Override
public AuthenticationChannel createAuthChannel(AuthenticationSequenceChannelType channel) {
return new LoginRecoveryAuthenticationChannel(channel);
}

@Override
protected Integer getOrder() {
return 10;
}
}

0 comments on commit 2a8e5fe

Please sign in to comment.