Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Oct 25, 2017
2 parents 39bb2e1 + 888e417 commit c652b08
Show file tree
Hide file tree
Showing 24 changed files with 537 additions and 93 deletions.
3 changes: 0 additions & 3 deletions gui/admin-gui/pom.xml
Expand Up @@ -527,7 +527,6 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<!-- <scope>runtime</scope> -->
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
Expand All @@ -536,12 +535,10 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<!--<scope>runtime</scope>-->
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<scope>runtime</scope>
</dependency>

<!-- WICKET DEPENDENCIES -->
Expand Down
Expand Up @@ -16,8 +16,21 @@

package com.evolveum.midpoint.web.boot;

import com.evolveum.midpoint.model.api.authentication.MidPointLdapAuthenticationProvider;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;

import java.util.Arrays;

/**
* Created by Viliam Repan (lazyman).
Expand All @@ -26,5 +39,55 @@
@Configuration
public class LdapSecurityConfig {

@Value("${auth.ldap.host}")
private String ldapHost;

@Value("${auth.ldap.manager}")
private String ldapUserDn;
@Value("${auth.ldap.manager.password}")
private String ldapUserPassword;

@Value("${auth.ldap.dn.pattern:}")
private String ldapDnPattern;

@Value("${auth.ldap.search.pattern:}")
private String ldapSearchPattern;

@Value("${auth.ldap.search.subtree}")
private boolean searchSubtree;

@Bean
public LdapContextSource contextSource() {
DefaultSpringSecurityContextSource ctx = new DefaultSpringSecurityContextSource(ldapHost);
ctx.setUserDn(ldapUserDn);
ctx.setPassword(ldapUserPassword);

return ctx;
}

@Bean
public MidPointLdapAuthenticationProvider midPointAuthenticationProvider(
@Qualifier("userDetailsService") UserDetailsContextMapper userDetailsContextMapper) {

BindAuthenticator auth = new BindAuthenticator(contextSource());
if (StringUtils.isNotEmpty(ldapDnPattern)) {
auth.setUserDnPatterns(new String[]{ldapDnPattern});
}
if (StringUtils.isNotEmpty(ldapSearchPattern)) {
auth.setUserSearch(userSearch());
}

MidPointLdapAuthenticationProvider provider = new MidPointLdapAuthenticationProvider(auth);
provider.setUserDetailsContextMapper(userDetailsContextMapper);

return provider;
}

@ConditionalOnProperty("auth.ldap.search.pattern")
@Bean
public FilterBasedLdapUserSearch userSearch() {
FilterBasedLdapUserSearch search = new FilterBasedLdapUserSearch("", ldapSearchPattern, contextSource());
search.setSearchSubtree(searchSubtree);
return search;
}
}
Expand Up @@ -82,7 +82,7 @@ public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/fonts/**");

web.ignoring().antMatchers("/wro/**");
web.ignoring().antMatchers("/less/**"); //todo should be there probably
web.ignoring().antMatchers("/less/**");

web.ignoring().antMatchers("/wicket/resource/**");
}
Expand Down
Expand Up @@ -203,8 +203,8 @@ public <O extends ObjectType> ObjectSecurityConstraints compileSecurityConstrain

@Override
public <T extends ObjectType, O extends ObjectType> ObjectFilter preProcessObjectFilter(String operationUrl, AuthorizationPhaseType phase,
Class<T> objectType, PrismObject<O> object, ObjectFilter origFilter, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
return securityEnforcer.preProcessObjectFilter(operationUrl, phase, objectType, object, origFilter, task, result);
Class<T> objectType, PrismObject<O> object, ObjectFilter origFilter, String limitAuthorizationAction, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
return securityEnforcer.preProcessObjectFilter(operationUrl, phase, objectType, object, origFilter, limitAuthorizationAction, task, result);
}

@Override
Expand Down
8 changes: 8 additions & 0 deletions gui/admin-gui/src/main/resources/application.yml
Expand Up @@ -2,5 +2,13 @@ spring:
application:
name: MidPoint

server:
display-name: MidPoint
session:
cookie:
max-age: 900
tomcat:
basedir: ${midpoint.home}

# more properties with default values can be found here:
# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
Expand Up @@ -182,10 +182,19 @@ public static boolean extractFilesFromClassPath(String srcPath, String dstPath,
return false;
}
URI srcUrl = src.toURI();
// URL srcUrl = ClassLoader.getSystemResource(srcPath);

String[] parts = srcUrl.toString().split("!/");
if (parts.length == 3
&& parts[1].equals("WEB-INF/classes")) {
// jar:file:<ABSOLUTE_PATH>/midpoint.war!/WEB-INF/classes!/initial-midpoint-home
srcUrl = URI.create(parts[0] + "!/" + parts[1] + "/" + parts[2]);
}

LOGGER.trace("URL: {}", srcUrl);
if (srcUrl.getPath().contains("!/")) {
URI srcFileUri = new URI(srcUrl.getPath().split("!/")[0]); // e.g. file:/C:/Documents%20and%20Settings/user/.m2/repository/com/evolveum/midpoint/infra/test-util/2.1-SNAPSHOT/test-util-2.1-SNAPSHOT.jar
if (srcUrl.toString().contains("!/")) {
String uri = srcUrl.toString().split("!/")[0].replace("jar:", "");
// file:<ABSOLUTE_PATH>/midpoint.war
URI srcFileUri = new URI(uri);
File srcFile = new File(srcFileUri);
JarFile jar = new JarFile(srcFile);
Enumeration<JarEntry> entries = jar.entries();
Expand Down
8 changes: 8 additions & 0 deletions model/model-api/pom.xml
Expand Up @@ -96,6 +96,14 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
Expand Down
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.evolveum.midpoint.model.api;

import com.evolveum.midpoint.security.api.ConnectionEnvironment;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

/**
* Created by Viliam Repan (lazyman).
*/
public interface ModelAuditRecorder {

void auditLoginSuccess(UserType user, ConnectionEnvironment connEnv);

void auditLoginFailure(String username, UserType user, ConnectionEnvironment connEnv, String message);

void auditLogout(ConnectionEnvironment connEnv, Task task);
}
Expand Up @@ -46,7 +46,7 @@ public enum ModelAuthorizationAction implements DisplayableValue<String> {
ASSIGN("assign", "Assign", "ASSIGN_HELP"),
UNASSIGN("unassign", "Unassign", "UNASSIGN_HELP"),
DELEGATE("delegate", "Delegate", "DELEGATE_HELP"),
ATTORNEY("attroney", "Attorney", "ATTRONEY_HELP"),
ATTORNEY("attorney", "Attorney", "ATTORNEY_HELP"),
EXECUTE_SCRIPT("executeScript", "Execute script", "EXECUTE_SCRIPT_HELP"),
CHANGE_CREDENTIALS("changeCredentials", "Change credentials", "CHANGE_CREDENTIALS_HELP"),

Expand Down
Expand Up @@ -22,6 +22,7 @@
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
import com.evolveum.midpoint.schema.result.OperationResult;
Expand Down Expand Up @@ -143,6 +144,25 @@ <F extends ObjectType> ModelContext<F> previewChanges(
*/
<F extends FocusType> RoleSelectionSpecification getAssignableRoleSpecification(PrismObject<F> focus, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException, ExpressionEvaluationException, CommunicationException, SecurityViolationException;

/**
* Returns filter for lookup of donors or power of attorney. The donors are the users that have granted
* the power of attorney to the currently logged-in user.
*
* TODO: authorization limitations
*
* @param searchResultType type of the expected search results
* @param origFilter original filter (e.g. taken from GUI search bar)
* @param targetAuthorizationAction Authorization action that the attorney is trying to execute
* on behalf of donor. Only donors for which the use of this authorization was
* not limited will be returned (that does not necessarily mean that the donor
* is able to execute this action, it may be limited by donor's authorizations).
* If the parameter is null then all donors are returned.
* @param task task
* @param parentResult operation result
* @return original filter with AND clause limiting the search.
*/
<T extends ObjectType> ObjectFilter getDonorFilter(Class<T> searchResultType, ObjectFilter origFilter, String targetAuthorizationAction, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException;

/**
* TODO
*
Expand Down
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.evolveum.midpoint.model.api.authentication;

import com.evolveum.midpoint.model.api.ModelAuditRecorder;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.security.api.ConnectionEnvironment;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.LdapAuthenticator;

public class MidPointLdapAuthenticationProvider extends LdapAuthenticationProvider {

private static final Trace LOGGER = TraceManager.getTrace(MidPointLdapAuthenticationProvider.class);

@Autowired
private ModelAuditRecorder auditProvider;

public MidPointLdapAuthenticationProvider(LdapAuthenticator authenticator) {
super(authenticator);
}

@Override
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken authentication) {

try {
return super.doAuthentication(authentication);
} catch (RuntimeException e) {
LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
auditProvider.auditLoginFailure(authentication.getName(), null, ConnectionEnvironment.create(SchemaConstants.CHANNEL_GUI_USER_URI), "bad credentials");
throw e;
}
}

@Override
protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication,
UserDetails user) {
Authentication authNCtx = super.createSuccessfulAuthentication(authentication, user);

Object principal = authNCtx.getPrincipal();
if (!(principal instanceof MidPointPrincipal)) {
throw new BadCredentialsException("LdapAuthentication.incorrect.value");
}
MidPointPrincipal midPointPrincipal = (MidPointPrincipal) principal;
UserType userType = midPointPrincipal.getUser();

if (userType == null) {
throw new BadCredentialsException("LdapAuthentication.bad.user");
}

auditProvider.auditLoginSuccess(userType, ConnectionEnvironment.create(SchemaConstants.CHANNEL_GUI_USER_URI));
return authNCtx;
}
}
Expand Up @@ -1741,13 +1741,13 @@ private <O extends ObjectType> ObjectQuery preProcessQuerySecurity(Class<O> obje
if (origQuery != null) {
origFilter = origQuery.getFilter();
}
ObjectFilter secFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.READ.getUrl(), null, objectType, null, origFilter, task, result);
ObjectFilter secFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.READ.getUrl(), null, objectType, null, origFilter, null, task, result);
return updateObjectQuery(origQuery, secFilter);
}

// we expect that objectType is a direct parent of containerType
private <C extends Containerable, O extends ObjectType> ObjectQuery preProcessSubobjectQuerySecurity(Class<C> containerType, Class<O> objectType, ObjectQuery origQuery, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
ObjectFilter secParentFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.READ.getUrl(), null, objectType, null, null, task, result);
ObjectFilter secParentFilter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.READ.getUrl(), null, objectType, null, null, null, task, result);
if (secParentFilter == null || secParentFilter instanceof AllFilter) {
return origQuery; // no need to update the query
}
Expand Down
Expand Up @@ -430,7 +430,7 @@ public <F extends FocusType> RoleSelectionSpecification getAssignableRoleSpecifi

try {
ObjectFilter filter = securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.ASSIGN.getUrl(),
AuthorizationPhaseType.REQUEST, AbstractRoleType.class, focus, AllFilter.createAll(), task, result);
AuthorizationPhaseType.REQUEST, AbstractRoleType.class, focus, AllFilter.createAll(), null, task, result);
LOGGER.trace("assignableRoleSpec filter: {}", filter);
spec.setFilter(filter);
if (filter instanceof NoneFilter) {
Expand Down Expand Up @@ -617,6 +617,10 @@ private RoleSelectionSpecEntry getRoleSelectionSpecEq(EqualFilter<String> eqFilt
}


@Override
public <T extends ObjectType> ObjectFilter getDonorFilter(Class<T> searchResultType, ObjectFilter origFilter, String targetAuthorizationAction, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
return securityEnforcer.preProcessObjectFilter(ModelAuthorizationAction.ATTORNEY.getUrl(), null, searchResultType, null, origFilter, targetAuthorizationAction, task, parentResult);
}

@Override
public <T extends ObjectType, O extends ObjectType> boolean canSearch(Class<T> resultType,
Expand Down

0 comments on commit c652b08

Please sign in to comment.