[CRV-12] Implementation completed

parent c5bd5249
......@@ -74,7 +74,9 @@ limitations under the License.
<properties>
<connid.version>1.4.2.0</connid.version>
<cxf.version>3.1.4</cxf.version>
<jackson.version>2.6.4</jackson.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
......@@ -98,7 +100,25 @@ limitations under the License.
<version>${connid.version}</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-local</artifactId>
<version>${cxf.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.tirasa.connid</groupId>
<artifactId>connector-test-common</artifactId>
......
/*
/*
* Copyright 2015 The CHOReVOLUTION project
*
* Licensed under the Apache License, Version 2.0 (the "License");
......@@ -15,6 +15,7 @@
*/
package eu.chorevolution.idm.connid.federationserver;
import java.net.URI;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.framework.common.exceptions.ConfigurationException;
import org.identityconnectors.framework.spi.AbstractConfiguration;
......@@ -22,22 +23,39 @@ import org.identityconnectors.framework.spi.ConfigurationProperty;
public class FederationServerConfiguration extends AbstractConfiguration {
private String sampleProperty = "SAMPLE VALUE";
private String baseAddress;
@ConfigurationProperty(displayMessageKey = "sampleProperty.display",
helpMessageKey = "sampleProperty.help", order = 1)
public String getSampleProperty() {
return sampleProperty;
private String domain;
@ConfigurationProperty(displayMessageKey = "baseAddress.display", helpMessageKey = "baseAddress.help", order = 1)
public String getBaseAddress() {
return baseAddress;
}
public void setBaseAddress(final String baseAddress) {
this.baseAddress = baseAddress;
}
public void setSampleProperty(final String sampleProperty) {
this.sampleProperty = sampleProperty;
@ConfigurationProperty(displayMessageKey = "domain.display", helpMessageKey = "domain.help", order = 2)
public String getDomain() {
return domain;
}
public void setDomain(final String domain) {
this.domain = domain;
}
@Override
public void validate() {
if (StringUtil.isBlank(sampleProperty)) {
throw new ConfigurationException("sampleProperty must not be blank!");
if (StringUtil.isBlank(baseAddress)) {
throw new ConfigurationException("baseAddress must not be blank!");
} else {
// verify that baseAddress is a valid URI
URI.create(baseAddress);
}
if (StringUtil.isBlank(domain)) {
throw new ConfigurationException("domain must not be blank!");
}
}
......
/*
* Copyright 2015 The CHOReVOLUTION project
*
* 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 eu.chorevolution.idm.connid.federationserver;
public final class FederationServerConstants {
public static final String GROUPS_NAME = "__GROUPS__";
private FederationServerConstants() {
// private constructor for static utility class
}
}
/*
/*
* Copyright 2015 The CHOReVOLUTION project
*
* Licensed under the Apache License, Version 2.0 (the "License");
......@@ -15,9 +15,16 @@
*/
package eu.chorevolution.idm.connid.federationserver;
/**
* This implementation depends on the logic that this connector follows.
*/
public class FederationServerFilter {
private final String username;
public FederationServerFilter(final String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
/*
* Copyright 2015 The CHOReVOLUTION project
*
* 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 eu.chorevolution.idm.connid.federationserver.api;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class EndUser implements Serializable {
private static final long serialVersionUID = -3901863997664130719L;
private String username;
private String password;
private boolean active = true;
private final Set<String> groups = new HashSet<>();
private final Map<String, Set<String>> attributes = new HashMap<>();
public String getUsername() {
return username;
}
public void setUsername(final String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public boolean isActive() {
return active;
}
public void setActive(final boolean active) {
this.active = active;
}
public void setPassword(final String password) {
this.password = password;
}
public Set<String> getGroups() {
return groups;
}
public Map<String, Set<String>> getAttributes() {
return attributes;
}
}
/*
* Copyright 2015 The CHOReVOLUTION project
*
* 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 eu.chorevolution.idm.connid.federationserver.api;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* REST operations for end-user management.
*/
@Path("/domains/{domain}/endusers")
public interface FederationServerEndUserService {
@GET
@Produces({ MediaType.APPLICATION_JSON })
List<EndUser> list(@PathParam("domain") String domain);
@POST
@Consumes({ MediaType.APPLICATION_JSON })
void create(@PathParam("domain") String domain, EndUser enduser);
@GET
@Path("{username}")
@Produces({ MediaType.APPLICATION_JSON })
EndUser read(@PathParam("domain") String domain, @PathParam("username") String username);
@PUT
@Path("{username}")
@Consumes({ MediaType.APPLICATION_JSON })
void update(@PathParam("domain") String domain, @PathParam("username") String username, EndUser enduser);
@DELETE
@Path("{username}")
void delete(@PathParam("domain") String domain, @PathParam("username") String username);
}
......@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
sampleProperty.display=Sampe Property
sampleProperty.help=Only a sample property
sample.connector.display=Sample Connector
baseAddress.display=Base Federation Server address
baseAddress.help=Base URI where the Federation Server exposes its end-user REST endpoint. If service is published at 'http://host:port/domains/domain1/endusers', then this property expected value is 'http://host:port'
federationserver.connector.display=CHOReVOLUTION: Federation Server Connector
domain.display=Federation Server domain
domain.help=Federation Server domain (for multi-tenancy environment)
......@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
sampleProperty.display=Propriet\u00e0 d'esempio
sampleProperty.help=Solo una propriet\u00e0 d'esempio
sample.connector.display=Connettore d'esempio
baseAddress.display=Indirizzo di base per il Federation Server
baseAddress.help=URI di base su cui il Federation Server pubblica il servizio REST per la gestione degli end-user. Se il servizio \u00e8 raggiungibile all'indirizzo 'http://host:port/domains/domain1/endusers', allora il valore atteso di questa propriet\u00e0 \u00e8 'http://host:port'
federationserver.connector.display=CHOReVOLUTION: Connettore Federation Server
domain.display=Dominio Federation Server
domain.help=Dominio Federation Server (per ambienti multi-tenancy)
/*
* 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 eu.chorevolution.idm.connid.federationserver;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import eu.chorevolution.idm.connid.federationserver.api.FederationServerEndUserService;
import java.util.ArrayList;
import java.util.List;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public abstract class AbstractTests {
public static final String BASE_ADDRESS = "local://federationServer";
private static Server SERVER;
@BeforeClass
public static void startServer() throws Exception {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setResourceClasses(FederationServerEndUserService.class);
List<Object> providers = new ArrayList<>();
providers.add(new JacksonJaxbJsonProvider());
sf.setProviders(providers);
sf.setResourceProvider(FederationServerEndUserService.class,
new SingletonResourceProvider(new TestFederationServerEndUserService(), true));
sf.setAddress(BASE_ADDRESS);
SERVER = sf.create();
}
@AfterClass
public static void stopServer() throws Exception {
SERVER.stop();
SERVER.destroy();
}
}
/*
* Copyright 2015 The CHOReVOLUTION project
*
* 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 eu.chorevolution.idm.connid.federationserver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.framework.api.APIConfiguration;
import org.identityconnectors.framework.api.ConnectorFacade;
import org.identityconnectors.framework.api.ConnectorFacadeFactory;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.test.common.TestHelpers;
import org.junit.Test;
public class ConnectorTests extends AbstractTests {
protected FederationServerConfiguration newConfiguration() {
FederationServerConfiguration conf = new FederationServerConfiguration();
conf.setBaseAddress(BASE_ADDRESS);
conf.setDomain("domain");
return conf;
}
protected ConnectorFacade newFacade() {
ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance();
APIConfiguration impl = TestHelpers.createTestConfiguration(FederationServerConnector.class, newConfiguration());
impl.getResultsHandlerConfiguration().setFilteredResultsHandlerInValidationMode(true);
return factory.newInstance(impl);
}
@Test
public void schema() {
ConnectorFacade connector = newFacade();
Schema schema = connector.schema();
assertEquals(1, schema.getObjectClassInfo().size());
ObjectClassInfo ocinfo = schema.getObjectClassInfo().iterator().next();
assertEquals(ObjectClass.ACCOUNT_NAME, ocinfo.getType());
assertEquals(2, ocinfo.getAttributeInfo().size());
int counter = 0;
for (AttributeInfo attrinfo : ocinfo.getAttributeInfo()) {
if (FederationServerConstants.GROUPS_NAME.equals(attrinfo.getName())) {
assertEquals(String.class, attrinfo.getType());
counter++;
} else if (Name.NAME.equals(attrinfo.getName())) {
assertTrue(attrinfo.isRequired());
counter++;
}
}
assertEquals(2, counter);
}
@Test
public void getObject() {
ConnectorFacade connector = newFacade();
ConnectorObject enduser = connector.getObject(
ObjectClass.ACCOUNT, new Uid(TestFederationServerEndUserService.DEFAULT_USERNAME), null);
assertNotNull(enduser);
assertEquals(new Uid(TestFederationServerEndUserService.DEFAULT_USERNAME), enduser.getUid());
assertEquals(new Name(TestFederationServerEndUserService.DEFAULT_USERNAME), enduser.getName());
assertTrue(AttributeUtil.isEnabled(enduser));
Attribute groups = enduser.getAttributeByName(FederationServerConstants.GROUPS_NAME);
assertNotNull(groups);
assertEquals(2, groups.getValue().size());
assertTrue(groups.getValue().contains("group1"));
assertTrue(groups.getValue().contains("group2"));
Attribute attr1 = AttributeUtil.find("attr1", enduser.getAttributes());
assertNotNull(attr1);
assertEquals(2, attr1.getValue().size());
assertTrue(attr1.getValue().contains("value1"));
assertTrue(attr1.getValue().contains("value2"));
}
@Test
public void getAllObjects() {
ConnectorFacade connector = newFacade();
List<ConnectorObject> list = new ArrayList<>();
connector.search(ObjectClass.ACCOUNT, null, list::add, null);
assertNotNull(list);
assertFalse(list.isEmpty());
list.stream().forEach((connectorObject) -> {
assertNotNull(connectorObject);
});
}
private Set<Attribute> getUniqueSample() {
String uuid = UUID.randomUUID().toString();
Set<Attribute> attrs = new HashSet<>();
attrs.add(new Name(uuid));
attrs.add(AttributeBuilder.buildPassword(uuid.toCharArray()));
attrs.add(AttributeBuilder.buildEnabled(true));
attrs.add(AttributeBuilder.build(FederationServerConstants.GROUPS_NAME, CollectionUtil.newSet("group1")));
attrs.add(AttributeBuilder.build("attrX", CollectionUtil.newSet("valueX")));
attrs.add(AttributeBuilder.build("attrY", CollectionUtil.newSet("valueX", "valueY")));
return attrs;
}
@Test
public void create() {
ConnectorFacade connector = newFacade();
Set<Attribute> attrs = getUniqueSample();
Uid uid = connector.create(ObjectClass.ACCOUNT, attrs, null);
assertNotNull(uid);
assertEquals(uid.getUidValue(), AttributeUtil.getNameFromAttributes(attrs).getNameValue());
ConnectorObject read = connector.getObject(ObjectClass.ACCOUNT, uid, null);
assertNotNull(read);
assertEquals(attrs, AttributeUtil.filterUid(read.getAttributes()));
}
@Test
public void update() {
ConnectorFacade connector = newFacade();
Set<Attribute> attrs = getUniqueSample();
Uid uid = connector.create(ObjectClass.ACCOUNT, attrs, null);
assertNotNull(uid);
assertEquals(uid.getUidValue(), AttributeUtil.getNameFromAttributes(attrs).getNameValue());
Attribute attrX = null;
for (Attribute attr : attrs) {
if ("attrX".equals(attr.getName())) {
attrX = attr;
}
}
attrs.remove(attrX);
attrX = AttributeBuilder.build("attrX", CollectionUtil.newSet("valueZ"));
attrs.add(attrX);
uid = connector.update(ObjectClass.ACCOUNT, uid, attrs, null);
assertNotNull(uid);
assertEquals(uid.getUidValue(), AttributeUtil.getNameFromAttributes(attrs).getNameValue());
ConnectorObject read = connector.getObject(ObjectClass.ACCOUNT, uid, null);
assertNotNull(read);
assertEquals(attrX, AttributeUtil.find("attrX", read.getAttributes()));
attrs.clear();
attrs.add(new Name("updated-" + uid.getUidValue()));
attrs.add(AttributeBuilder.buildEnabled(false));
uid = connector.update(ObjectClass.ACCOUNT, uid, attrs, null);
assertNotNull(uid);
assertEquals(uid.getUidValue(), AttributeUtil.getNameFromAttributes(attrs).getNameValue());
}
@Test
public void delete() {
ConnectorFacade connector = newFacade();
Set<Attribute> attrs = getUniqueSample();
Uid uid = connector.create(ObjectClass.ACCOUNT, attrs, null);
assertNotNull(uid);
assertEquals(uid.getUidValue(), AttributeUtil.getNameFromAttributes(attrs).getNameValue());
connector.delete(ObjectClass.ACCOUNT, uid, null);
ConnectorObject read = connector.getObject(ObjectClass.ACCOUNT, uid, null);
assertNull(read);
}
}
/*
* Copyright 2015 The CHOReVOLUTION project
*
* 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 eu.chorevolution.idm.connid.federationserver;
import eu.chorevolution.idm.connid.federationserver.FederationServerConfiguration;
import eu.chorevolution.idm.connid.federationserver.FederationServerConnector;
import static org.junit.Assert.assertNotNull;
import java.util.Collections;
import org.identityconnectors.framework.api.APIConfiguration;
import org.identityconnectors.framework.api.ConnectorFacade;
import org.identityconnectors.framework.api.ConnectorFacadeFactory;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.test.common.TestHelpers;
import org.junit.Test;
public class FederationServerTests {
protected FederationServerConfiguration newConfiguration() {
return new FederationServerConfiguration();
}
protected ConnectorFacade newFacade() {
ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance();
APIConfiguration impl = TestHelpers.createTestConfiguration(FederationServerConnector.class, newConfiguration());
impl.getResultsHandlerConfiguration().setFilteredResultsHandlerInValidationMode(true);
return factory.newInstance(impl);
}
@Test
public void basic() {
Uid created = newFacade().create(
ObjectClass.ACCOUNT,
Collections.<Attribute>emptySet(),
new OperationOptionsBuilder().build());
assertNotNull(created);
}
}
/*
* 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 eu.chorevolution.idm.connid.federationserver;
import eu.chorevolution.idm.connid.federationserver.api.EndUser;
import eu.chorevolution.idm.connid.federationserver.api.FederationServerEndUserService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.identityconnectors.common.CollectionUtil;
public class TestFederationServerEndUserService implements FederationServerEndUserService {
public static final String DEFAULT_USERNAME = "test";
private final Map<String, EndUser> endusers = new HashMap<>();
public TestFederationServerEndUserService() {
EndUser endUser = new EndUser();
endUser.setUsername(DEFAULT_USERNAME);
endUser.setPassword("password");
endUser.setActive(true);
endUser.getGroups().add("group1");
endUser.getGroups().add("group2");
endUser.getAttributes().put("attr1", CollectionUtil.newSet("value1", "value2"));
endUser.getAttributes().put("attr2", CollectionUtil.newSet("value3", "value4"));