[SYNCOPE-1015] Added the conf parameter 'authentication.attributes' to use alternate attrs for authentication
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/8c963580 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/8c963580 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/8c963580 Branch: refs/heads/master Commit: 8c9635805a72540541eb960cbd799e1a8b1a90eb Parents: e96d8ab Author: Francesco Chicchiriccò <ilgro...@apache.org> Authored: Wed Feb 15 12:57:12 2017 +0100 Committer: Francesco Chicchiriccò <ilgro...@apache.org> Committed: Wed Feb 15 12:57:34 2017 +0100 ---------------------------------------------------------------------- .../main/resources/domains/MasterContent.xml | 9 ++ .../persistence/jpa/inner/PlainSchemaTest.java | 2 +- .../test/resources/domains/MasterContent.xml | 9 ++ .../core/spring/security/AuthDataAccessor.java | 144 ++++++++++--------- .../security/SyncopeAuthenticationProvider.java | 77 ++++++---- .../security/SyncopeUserDetailsService.java | 37 ----- .../src/main/resources/securityContext.xml | 3 - .../apache/syncope/fit/core/UserSelfITCase.java | 4 +- .../configurationparameters.adoc | 2 + 9 files changed, 151 insertions(+), 136 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/persistence-jpa/src/main/resources/domains/MasterContent.xml ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml index 5b6cab9..e208c70 100644 --- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml @@ -90,6 +90,15 @@ under the License. <CPlainAttrValue id="b5e8e79d-8039-4318-9698-fe5e181ebe98" attribute_id="e5a712ad-53fd-4102-ba55-fb45caed5f7b" booleanValue="1"/> + <SyncopeSchema id="authentication.attributes"/> + <PlainSchema id="authentication.attributes" type="String" multivalue="1" uniqueConstraint="0" readonly="0"/> + <CPlainAttr id="577c6c4d-7149-43c2-9821-9ab4510effbd" + owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="authentication.attributes"/> + <CPlainAttrValue id="c13660b5-926c-47ea-946d-52db32dd492b" + attribute_id="577c6c4d-7149-43c2-9821-9ab4510effbd" stringValue="username"/> + <CPlainAttrValue id="0f112dbd-00d4-441c-b732-331e7f348f8a" + attribute_id="577c6c4d-7149-43c2-9821-9ab4510effbd" stringValue="email"/> + <SyncopeSchema id="authentication.statuses"/> <PlainSchema id="authentication.statuses" type="String" multivalue="1" uniqueConstraint="0" readonly="0"/> <CPlainAttr id="888ae8e1-a295-4ee2-a15e-31dbf6dfc3f9" http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java index 918f9fc..b20bc4b 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java @@ -47,7 +47,7 @@ public class PlainSchemaTest extends AbstractTest { @Test public void findAll() { List<PlainSchema> schemas = plainSchemaDAO.findAll(); - assertEquals(39, schemas.size()); + assertEquals(40, schemas.size()); } @Test http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/persistence-jpa/src/test/resources/domains/MasterContent.xml ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml index 28c6715..7337f4f 100644 --- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml @@ -90,6 +90,15 @@ under the License. <CPlainAttrValue id="b5e8e79d-8039-4318-9698-fe5e181ebe98" attribute_id="e5a712ad-53fd-4102-ba55-fb45caed5f7b" booleanValue="1"/> + <SyncopeSchema id="authentication.attributes"/> + <PlainSchema id="authentication.attributes" type="String" multivalue="1" uniqueConstraint="0" readonly="0"/> + <CPlainAttr id="577c6c4d-7149-43c2-9821-9ab4510effbd" + owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="authentication.attributes"/> + <CPlainAttrValue id="c13660b5-926c-47ea-946d-52db32dd492b" + attribute_id="577c6c4d-7149-43c2-9821-9ab4510effbd" stringValue="username"/> + <CPlainAttrValue id="0f112dbd-00d4-441c-b732-331e7f348f8a" + attribute_id="577c6c4d-7149-43c2-9821-9ab4510effbd" stringValue="email"/> + <SyncopeSchema id="authentication.statuses"/> <PlainSchema id="authentication.statuses" type="String" multivalue="1" uniqueConstraint="0" readonly="0"/> <CPlainAttr id="888ae8e1-a295-4ee2-a15e-31dbf6dfc3f9" http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java ---------------------------------------------------------------------- diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java index 377b96e..e8a919d 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java @@ -19,10 +19,12 @@ package org.apache.syncope.core.spring.security; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; @@ -33,10 +35,10 @@ import org.apache.commons.collections4.SetUtils; import org.apache.commons.collections4.Transformer; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.common.lib.types.StandardEntitlement; -import org.apache.syncope.core.provisioning.api.EntitlementsHolder; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.ConfDAO; @@ -44,6 +46,8 @@ import org.apache.syncope.core.persistence.api.dao.DomainDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Domain; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; @@ -61,7 +65,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.transaction.annotation.Transactional; /** @@ -101,6 +104,9 @@ public class AuthDataAccessor { protected AnyTypeDAO anyTypeDAO; @Autowired + protected AnySearchDAO searchDAO; + + @Autowired protected ConnectorFactory connFactory; @Autowired @@ -126,13 +132,32 @@ public class AuthDataAccessor { * @return {@code null} if no matching user was found, authentication result otherwise */ @Transactional(noRollbackFor = DisabledException.class) - public Pair<String, Boolean> authenticate(final Authentication authentication) { - String key = null; - Boolean authenticated = null; + public Pair<User, Boolean> authenticate(final Authentication authentication) { + User user = null; + + CPlainAttr authAttrs = confDAO.find("authentication.attributes"); + List<String> authAttrValues = authAttrs == null + ? Collections.singletonList("username") + : authAttrs.getValuesAsStrings(); + for (int i = 0; user == null && i < authAttrValues.size(); i++) { + if ("username".equals(authAttrValues.get(i))) { + user = userDAO.findByUsername(authentication.getName()); + } else { + AttributeCond attrCond = new AttributeCond(AttributeCond.Type.EQ); + attrCond.setSchema(authAttrValues.get(i)); + attrCond.setExpression(authentication.getName()); + List<User> users = searchDAO.search(SearchCond.getLeafCond(attrCond), AnyTypeKind.USER); + if (users.size() == 1) { + user = users.get(0); + } else { + LOG.warn("Value {} provided for {} does not uniquely identifies an user", + authentication.getName(), authAttrValues.get(i)); + } + } + } - User user = userDAO.findByUsername(authentication.getName()); + Boolean authenticated = null; if (user != null) { - key = user.getKey(); authenticated = false; if (user.isSuspended() != null && user.isSuspended()) { @@ -167,7 +192,7 @@ public class AuthDataAccessor { } } - return ImmutablePair.of(key, authenticated); + return ImmutablePair.of(user, authenticated); } protected boolean authenticate(final User user, final String password) { @@ -239,79 +264,60 @@ public class AuthDataAccessor { } @Transactional - public Set<SyncopeGrantedAuthority> load(final String username) { - final Set<SyncopeGrantedAuthority> authorities = new HashSet<>(); - if (anonymousUser.equals(username)) { - authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.ANONYMOUS)); - } else if (adminUser.equals(username)) { - CollectionUtils.collect( - EntitlementsHolder.getInstance().getValues(), - new Transformer<String, SyncopeGrantedAuthority>() { - - @Override - public SyncopeGrantedAuthority transform(final String entitlement) { - return new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM); - } - }, authorities); + public Set<SyncopeGrantedAuthority> getAuthorities(final User user) { + Set<SyncopeGrantedAuthority> authorities = new HashSet<>(); + if (user.isMustChangePassword()) { + authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD)); } else { - User user = userDAO.findByUsername(username); - if (user == null) { - throw new UsernameNotFoundException("Could not find any user with id " + username); - } - - if (user.isMustChangePassword()) { - authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD)); - } else { - final Map<String, Set<String>> entForRealms = new HashMap<>(); - - // Give entitlements as assigned by roles (with realms, where applicable) - assigned either - // statically and dynamically - for (final Role role : userDAO.findAllRoles(user)) { - IterableUtils.forEach(role.getEntitlements(), new Closure<String>() { - - @Override - public void execute(final String entitlement) { - Set<String> realms = entForRealms.get(entitlement); - if (realms == null) { - realms = new HashSet<>(); - entForRealms.put(entitlement, realms); - } - - CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() { + final Map<String, Set<String>> entForRealms = new HashMap<>(); - @Override - public String transform(final Realm realm) { - return realm.getFullPath(); - } - }, realms); - } - }); - } - - // Give group entitlements for owned groups - for (Group group : groupDAO.findOwnedByUser(user.getKey())) { - for (String entitlement : Arrays.asList( - StandardEntitlement.GROUP_READ, - StandardEntitlement.GROUP_UPDATE, - StandardEntitlement.GROUP_DELETE)) { + // Give entitlements as assigned by roles (with realms, where applicable) - assigned either + // statically and dynamically + for (final Role role : userDAO.findAllRoles(user)) { + IterableUtils.forEach(role.getEntitlements(), new Closure<String>() { + @Override + public void execute(final String entitlement) { Set<String> realms = entForRealms.get(entitlement); if (realms == null) { realms = new HashSet<>(); entForRealms.put(entitlement, realms); } - realms.add(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())); + CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() { + + @Override + public String transform(final Realm realm) { + return realm.getFullPath(); + } + }, realms); + } + }); + } + + // Give group entitlements for owned groups + for (Group group : groupDAO.findOwnedByUser(user.getKey())) { + for (String entitlement : Arrays.asList( + StandardEntitlement.GROUP_READ, + StandardEntitlement.GROUP_UPDATE, + StandardEntitlement.GROUP_DELETE)) { + + Set<String> realms = entForRealms.get(entitlement); + if (realms == null) { + realms = new HashSet<>(); + entForRealms.put(entitlement, realms); } - } - // Finally normalize realms for each given entitlement and generate authorities - for (Map.Entry<String, Set<String>> entry : entForRealms.entrySet()) { - SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(entry.getKey()); - authority.addRealms(RealmUtils.normalize(entry.getValue())); - authorities.add(authority); + realms.add(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())); } } + + // Finally normalize realms for each given entitlement and generate authorities + for (Map.Entry<String, Set<String>> entry : entForRealms.entrySet()) { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(entry.getKey()); + authority.addRealms(RealmUtils.normalize(entry.getValue())); + authorities.add(authority); + } } return authorities; http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java index 369b92c..9f6a3ed 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationProvider.java @@ -18,15 +18,22 @@ */ package org.apache.syncope.core.spring.security; +import java.util.HashSet; +import java.util.Set; import javax.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.common.lib.types.AuditElements.Result; import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.StandardEntitlement; import org.apache.syncope.core.spring.security.AuthContextUtils.Executable; import org.apache.syncope.core.persistence.api.entity.Domain; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.EntitlementsHolder; import org.apache.syncope.core.provisioning.api.UserProvisioningManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +43,6 @@ import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetailsService; @Configurable public class SyncopeAuthenticationProvider implements AuthenticationProvider { @@ -61,8 +67,6 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { protected String anonymousKey; - protected UserDetailsService userDetailsService; - protected final Encryptor encryptor = Encryptor.getInstance(); /** @@ -86,10 +90,6 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { this.anonymousKey = anonymousKey; } - public void setUserDetailsService(final UserDetailsService syncopeUserDetailsService) { - this.userDetailsService = syncopeUserDetailsService; - } - @Override public Authentication authenticate(final Authentication authentication) { String domainKey = SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain(); @@ -98,10 +98,18 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { } SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).setDomain(domainKey); + final String[] username = new String[1]; Boolean authenticated; + final Set<SyncopeGrantedAuthority> authorities = new HashSet<>(); + if (anonymousUser.equals(authentication.getName())) { + username[0] = anonymousUser; authenticated = authentication.getCredentials().toString().equals(anonymousKey); + if (authenticated) { + authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.ANONYMOUS)); + } } else if (adminUser.equals(authentication.getName())) { + username[0] = adminUser; if (SyncopeConstants.MASTER_DOMAIN.equals(domainKey)) { authenticated = encryptor.verify( authentication.getCredentials().toString(), @@ -123,27 +131,47 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { } }); } + if (authenticated) { + CollectionUtils.collect( + EntitlementsHolder.getInstance().getValues(), + new Transformer<String, SyncopeGrantedAuthority>() { + + @Override + public SyncopeGrantedAuthority transform(final String entitlement) { + return new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM); + } + }, authorities); + } } else { - final Pair<String, Boolean> authResult = - AuthContextUtils.execWithAuthContext(domainKey, new Executable<Pair<String, Boolean>>() { + final Pair<User, Boolean> authResult = + AuthContextUtils.execWithAuthContext(domainKey, new Executable<Pair<User, Boolean>>() { @Override - public Pair<String, Boolean> exec() { + public Pair<User, Boolean> exec() { return dataAccessor.authenticate(authentication); } }); authenticated = authResult.getValue(); - if (authenticated != null && !authenticated) { - AuthContextUtils.execWithAuthContext(domainKey, new Executable<Void>() { + if (authResult.getLeft() != null && authResult.getRight() != null) { + username[0] = authResult.getLeft().getUsername(); - @Override - public Void exec() { - provisioningManager.internalSuspend(authResult.getKey()); - return null; - } - }); + if (authResult.getRight()) { + authorities.addAll(dataAccessor.getAuthorities(authResult.getLeft())); + } else { + AuthContextUtils.execWithAuthContext(domainKey, new Executable<Void>() { + + @Override + public Void exec() { + provisioningManager.internalSuspend(authResult.getLeft().getKey()); + return null; + } + }); + } } } + if (username[0] == null) { + username[0] = authentication.getPrincipal().toString(); + } final boolean isAuthenticated = authenticated != null && authenticated; UsernamePasswordAuthenticationToken token; @@ -154,10 +182,9 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { @Override public UsernamePasswordAuthenticationToken exec() { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - authentication.getPrincipal(), + username[0], null, - userDetailsService.loadUserByUsername(authentication.getPrincipal().toString()). - getAuthorities()); + authorities); token.setDetails(authentication.getDetails()); dataAccessor.audit(AuditElements.EventCategoryType.LOGIC, @@ -174,7 +201,7 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { }); LOG.debug("User {} successfully authenticated, with entitlements {}", - authentication.getPrincipal(), token.getAuthorities()); + username[0], token.getAuthorities()); } else { AuthContextUtils.execWithAuthContext(domainKey, new Executable<Void>() { @@ -188,14 +215,14 @@ public class SyncopeAuthenticationProvider implements AuthenticationProvider { null, isAuthenticated, authentication, - "User " + authentication.getPrincipal() + " not authenticated"); + "User " + username[0] + " not authenticated"); return null; } }); - LOG.debug("User {} not authenticated", authentication.getPrincipal()); + LOG.debug("User {} not authenticated", username[0]); - throw new BadCredentialsException("User " + authentication.getPrincipal() + " not authenticated"); + throw new BadCredentialsException("User " + username[0] + " not authenticated"); } return token; http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeUserDetailsService.java ---------------------------------------------------------------------- diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeUserDetailsService.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeUserDetailsService.java deleted file mode 100644 index 544fc99..0000000 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeUserDetailsService.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.syncope.core.spring.security; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Configurable; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; - -@Configurable -public class SyncopeUserDetailsService implements UserDetailsService { - - @Autowired - protected AuthDataAccessor dataAccessor; - - @Override - public UserDetails loadUserByUsername(final String username) { - return new User(username, "<PASSWORD_PLACEHOLDER>", dataAccessor.load(username)); - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/core/spring/src/main/resources/securityContext.xml ---------------------------------------------------------------------- diff --git a/core/spring/src/main/resources/securityContext.xml b/core/spring/src/main/resources/securityContext.xml index 83b3468..3aa6079 100644 --- a/core/spring/src/main/resources/securityContext.xml +++ b/core/spring/src/main/resources/securityContext.xml @@ -86,14 +86,11 @@ under the License. <bean class="org.apache.syncope.core.spring.security.AuthDataAccessor"/> - <bean id="syncopeUserDetailsService" class="org.apache.syncope.core.spring.security.SyncopeUserDetailsService"/> - <bean id="syncopeAuthenticationProvider" class="org.apache.syncope.core.spring.security.SyncopeAuthenticationProvider"> <property name="adminPassword" value="${adminPassword}"/> <property name="adminPasswordAlgorithm" value="${adminPasswordAlgorithm}"/> <property name="anonymousKey" value="${anonymousKey}"/> - <property name="userDetailsService" ref="syncopeUserDetailsService"/> </bean> <security:authentication-manager> http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java index b0225d8..fef308f 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java @@ -28,7 +28,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.security.AccessControlException; import java.util.Map; import java.util.Set; import javax.sql.DataSource; @@ -151,6 +150,9 @@ public class UserSelfITCase extends AbstractITCase { Pair<Map<String, Set<String>>, UserTO> self = clientFactory.create("rossini", ADMIN_PWD).self(); assertEquals("rossini", self.getValue().getUsername()); + + Pair<Map<String, Set<String>>, UserTO> byEmail = clientFactory.create("ve...@syncope.org", ADMIN_PWD).self(); + assertEquals("verdi", byEmail.getValue().getUsername()); } @Test http://git-wip-us.apache.org/repos/asf/syncope/blob/8c963580/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/configurationparameters.adoc ---------------------------------------------------------------------- diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/configurationparameters.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/configurationparameters.adoc index e28f4bc..b052ab8 100644 --- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/configurationparameters.adoc +++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/configurationparameters.adoc @@ -40,6 +40,8 @@ processes, including <<password-reset,password reset>>; * `passwordReset.allowed` - whether the <<password-reset,password reset>> feature (typically via the enduser application) is allowed; * `passwordReset.securityQuestion` - whether the <<password-reset,password reset>> feature involves security questions; +* `authentication.attributes` - the list of attributes whose values can be passed as login name for authentication; +defaults to `username` * `authentication.statuses` - the list of <<workflow,workflow>> statuses for which users are allowed to authenticate; [WARNING] Suspended Users are anyway not allowed to authenticate.