added OAuth2 authentication for REST protocol

Change-Id: I3b8f3943ea043587730870a0b861760a4d6f3aa7
This commit is contained in:
fahadnaeemkhan 2017-12-01 19:49:45 -08:00 committed by Yuta HIGUCHI
parent 28d4657304
commit 02ffa7120d
13 changed files with 159 additions and 35 deletions

View File

@ -547,6 +547,15 @@ remote_jar (
visibility = [ 'PUBLIC' ],
)
remote_jar (
name = 'jersey-security',
out = 'oauth2-client-2.25.1.jar',
url = 'mvn:org.glassfish.jersey.security:oauth2-client:jar:2.25.1',
sha1 = '5081be1cdc45a48ebeada89157cab4711f7bad1b',
maven_coords = 'org.glassfish.jersey.security:oauth2-client:jar:NON-OSGI:2.25.1',
visibility = [ 'PUBLIC' ],
)
remote_jar (
name = 'jersey-common',
out = 'jersey-common-2.25.1.jar',

View File

@ -155,6 +155,7 @@
"javax.inject": "mvn:org.glassfish.hk2.external:javax.inject:2.5.0-b32",
"javax.ws.rs-api": "mvn:javax.ws.rs:javax.ws.rs-api:2.1",
"jersey-client": "mvn:org.glassfish.jersey.core:jersey-client:2.25.1",
"jersey-security": "mvn:org.glassfish.jersey.security:oauth2-client:jar:2.25.1",
"jersey-common": "mvn:org.glassfish.jersey.core:jersey-common:2.25.1",
"jersey-container-jetty-http": "mvn:org.glassfish.jersey.containers:jersey-container-jetty-http:2.25.1",
"jersey-container-servlet": "mvn:org.glassfish.jersey.containers:jersey-container-servlet:2.25.1",

View File

@ -223,6 +223,13 @@
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>oauth2-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>

View File

@ -3,6 +3,7 @@ COMPILE_DEPS = [
'//utils/rest:onlab-rest',
'//lib:CORE_DEPS',
'//lib:jersey-client',
'//lib:jersey-security',
'//lib:jersey-common',
'//lib:httpclient-osgi',
'//lib:httpcore-osgi',

View File

@ -35,6 +35,12 @@
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-osgi</artifactId>
@ -43,7 +49,6 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>

View File

@ -25,10 +25,12 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.protocol.http.HttpSBController;
import org.onosproject.protocol.rest.RestSBDevice;
import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -55,6 +57,8 @@ import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The implementation of HttpSBController.
*/
@ -102,11 +106,7 @@ public class HttpSBControllerImpl implements HttpSBController {
public void addDevice(RestSBDevice device) {
if (!deviceMap.containsKey(device.deviceId())) {
Client client = ignoreSslClient();
if (device.username() != null) {
String username = device.username();
String password = device.password() == null ? "" : device.password();
authenticate(client, username, password);
}
authenticate(client, device);
clientMap.put(device.deviceId(), client);
deviceMap.put(device.deviceId(), device);
} else {
@ -283,8 +283,23 @@ public class HttpSBControllerImpl implements HttpSBController {
}
}
private void authenticate(Client client, String username, String password) {
client.register(HttpAuthenticationFeature.basic(username, password));
private void authenticate(Client client, RestSBDevice device) {
AuthenticationScheme authScheme = device.authentication();
if (authScheme == AuthenticationScheme.NO_AUTHENTICATION) {
log.debug("{} scheme is specified, ignoring authentication", authScheme);
return;
} else if (authScheme == AuthenticationScheme.OAUTH2) {
String token = checkNotNull(device.token());
client.register(OAuth2ClientSupport.feature(token));
} else if (authScheme == AuthenticationScheme.BASIC) {
String username = device.username();
String password = device.password() == null ? "" : device.password();
client.register(HttpAuthenticationFeature.basic(username, password));
} else {
// TODO: Add support for other authentication schemes here.
throw new IllegalArgumentException(String.format("Unsupported authentication scheme: %s",
authScheme.name()));
}
}
protected WebTarget getWebTarget(DeviceId device, String request) {
@ -343,12 +358,15 @@ public class HttpSBControllerImpl implements HttpSBController {
try {
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}

View File

@ -41,6 +41,8 @@ public class DefaultRestSBDevice implements RestSBDevice {
private String protocol;
private String url;
private boolean isProxy;
private AuthenticationScheme authenticationScheme;
private String token;
private final Optional<String> testUrl;
private final Optional<String> manufacturer;
private final Optional<String> hwVersion;
@ -48,13 +50,13 @@ public class DefaultRestSBDevice implements RestSBDevice {
public DefaultRestSBDevice(IpAddress ip, int port, String name, String password,
String protocol, String url, boolean isActive) {
this(ip, port, name, password, protocol, url, isActive, "", "", "", "");
this(ip, port, name, password, protocol, url, isActive, "", "", "", "", AuthenticationScheme.BASIC, "");
}
public DefaultRestSBDevice(IpAddress ip, int port, String name, String password,
String protocol, String url, boolean isActive, String testUrl, String manufacturer,
String hwVersion,
String swVersion) {
String hwVersion, String swVersion, AuthenticationScheme authenticationScheme,
String token) {
Preconditions.checkNotNull(ip, "IP address cannot be null");
Preconditions.checkArgument(port > 0, "Port address cannot be negative");
Preconditions.checkNotNull(protocol, "protocol address cannot be null");
@ -65,6 +67,8 @@ public class DefaultRestSBDevice implements RestSBDevice {
this.isActive = isActive;
this.protocol = protocol;
this.url = StringUtils.isEmpty(url) ? null : url;
this.authenticationScheme = authenticationScheme;
this.token = token;
this.manufacturer = StringUtils.isEmpty(manufacturer) ?
Optional.empty() : Optional.ofNullable(manufacturer);
this.hwVersion = StringUtils.isEmpty(hwVersion) ?
@ -158,6 +162,16 @@ public class DefaultRestSBDevice implements RestSBDevice {
return swVersion;
}
@Override
public AuthenticationScheme authentication() {
return authenticationScheme;
}
@Override
public String token() {
return token;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@ -168,6 +182,8 @@ public class DefaultRestSBDevice implements RestSBDevice {
.add("username", username)
.add("port", port)
.add("ip", ip)
.add("authentication", authenticationScheme.name())
.add("token", token)
.add("manufacturer", manufacturer.orElse(null))
.add("hwVersion", hwVersion.orElse(null))
.add("swVersion", swVersion.orElse(null))

View File

@ -25,6 +25,16 @@ import java.util.Optional;
* Represents an abstraction of a Rest Device in ONOS.
*/
public interface RestSBDevice {
/**
* REST Authentication schemes.
*/
public enum AuthenticationScheme {
NO_AUTHENTICATION,
BASIC,
OAUTH,
OAUTH2,
}
/**
* Returns the ip of this device.
*
@ -39,6 +49,20 @@ public interface RestSBDevice {
*/
int port();
/**
* The authentication scheme of rest device.
*
* @return authentication
*/
AuthenticationScheme authentication();
/**
* The access token of rest device if authentication is OAuth2.
*
* @return token
*/
String token();
/**
* Returns the username of this device.
*
@ -91,6 +115,7 @@ public interface RestSBDevice {
/**
* Returns the proxy state of this device
* (if true, the device is proxying multiple ONOS devices).
*
* @return proxy state
*/
boolean isProxy();
@ -122,4 +147,5 @@ public interface RestSBDevice {
* @return the software version.
*/
Optional<String> swVersion();
}

View File

@ -3,6 +3,7 @@ BUNDLES = [
'//protocols/rest/api:onos-protocols-rest-api',
'//protocols/rest/ctl:onos-protocols-rest-ctl',
'//lib:jersey-client',
'//lib:jersey-security',
'//lib:commons-io',
'//lib:httpclient-osgi',
'//lib:httpcore-osgi',

View File

@ -21,6 +21,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.Config;
import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
/**
* Configuration to push devices to the REST provider.
@ -38,11 +39,14 @@ public class RestDeviceConfig extends Config<DeviceId> {
private static final String MANUFACTURER = "manufacturer";
private static final String HWVERSION = "hwVersion";
private static final String SWVERSION = "swVersion";
private static final String AUTHENTICATION_SCHEME = "authenticationScheme";
private static final String TOKEN = "token";
@Override
public boolean isValid() {
return hasOnlyFields(IP, PORT, USERNAME, PASSWORD, PROTOCOL, URL,
TESTURL, MANUFACTURER, HWVERSION, SWVERSION) &&
TESTURL, MANUFACTURER, HWVERSION, SWVERSION, AUTHENTICATION_SCHEME,
TOKEN) &&
ip() != null;
}
@ -136,6 +140,31 @@ public class RestDeviceConfig extends Config<DeviceId> {
return get(SWVERSION, "");
}
/**
* Gets the authentication type of the REST device.
* Default is 'basic' if username is defined, else default is no_authentication.
*
* @return authentication
*/
public AuthenticationScheme authenticationScheme() {
// hack for backward compatibility
if (!hasField(AUTHENTICATION_SCHEME)) {
if (hasField(USERNAME)) {
return AuthenticationScheme.BASIC;
}
}
return AuthenticationScheme.valueOf(get(AUTHENTICATION_SCHEME, "NO_AUTHENTICATION").toUpperCase());
}
/**
* Gets the token of the REST device.
*
* @return token
*/
public String token() {
return get(TOKEN, "");
}
private Pair<String, Integer> extractIpPort() {
String info = subject.toString();
if (info.startsWith(RestDeviceProvider.REST)) {

View File

@ -28,8 +28,6 @@ import org.onlab.util.SharedScheduledExecutorService;
import org.onlab.util.SharedScheduledExecutors;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.behaviour.PortAdmin;
import org.onosproject.net.config.ConfigException;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
@ -38,7 +36,9 @@ import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.behaviour.DevicesDiscovery;
import org.onosproject.net.behaviour.PortAdmin;
import org.onosproject.net.behaviour.PortDiscovery;
import org.onosproject.net.config.ConfigException;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
@ -73,6 +73,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -81,7 +82,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.concurrent.CompletableFuture;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.groupedThreads;
@ -354,22 +354,25 @@ public class RestDeviceProvider extends AbstractProvider
Set<DeviceId> deviceSubjects =
cfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
connectDevices(deviceSubjects.stream()
.filter(deviceId -> deviceService.getDevice(deviceId) == null)
.map(deviceId -> {
RestDeviceConfig config =
cfgService.getConfig(deviceId, RestDeviceConfig.class);
return new DefaultRestSBDevice(config.ip(),
config.port(),
config.username(),
config.password(),
config.protocol(),
config.url(),
false,
config.testUrl(),
config.manufacturer(),
config.hwVersion(),
config.swVersion());
}).collect(Collectors.toSet()));
.filter(deviceId -> deviceService.getDevice(deviceId) == null)
.map(deviceId -> {
RestDeviceConfig config =
cfgService.getConfig(deviceId, RestDeviceConfig.class);
return new DefaultRestSBDevice(config.ip(),
config.port(),
config.username(),
config.password(),
config.protocol(),
config.url(),
false,
config.testUrl(),
config.manufacturer(),
config.hwVersion(),
config.swVersion(),
config.authenticationScheme(),
config.token()
);
}).collect(Collectors.toSet()));
}
//Old method to register devices provided via net-cfg under apps/rest/ tree

View File

@ -21,15 +21,17 @@ import com.google.common.annotations.Beta;
import com.google.common.collect.Sets;
import org.onlab.packet.IpAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.config.ConfigException;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.ConfigException;
import org.onosproject.protocol.rest.DefaultRestSBDevice;
import org.onosproject.protocol.rest.RestSBDevice;
import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
import java.util.Set;
/**
* Configuration for RestSB provider.
*
* @deprecated 1.10.0 Kingfisher. Please Use RestDeviceConfig
*/
@Deprecated
@ -48,6 +50,8 @@ public class RestProviderConfig extends Config<ApplicationId> {
private static final String MANUFACTURER = "manufacturer";
private static final String HWVERSION = "hwVersion";
private static final String SWVERSION = "swVersion";
private static final String AUTHENTICATION_SCHEME = "authenticationScheme";
private static final String TOKEN = "token";
public Set<RestSBDevice> getDevicesAddresses() throws ConfigException {
Set<RestSBDevice> devicesAddresses = Sets.newHashSet();
@ -65,11 +69,14 @@ public class RestProviderConfig extends Config<ApplicationId> {
String manufacturer = node.path(MANUFACTURER).asText();
String hwVersion = node.path(HWVERSION).asText();
String swVersion = node.path(SWVERSION).asText();
AuthenticationScheme authenticationScheme = AuthenticationScheme.valueOf(node.path(
AUTHENTICATION_SCHEME).asText().toUpperCase());
String token = node.path(TOKEN).asText();
devicesAddresses.add(new DefaultRestSBDevice(ipAddr, port, username,
password, protocol,
url, false, testUrl, manufacturer,
hwVersion, swVersion));
password, protocol,
url, false, testUrl, manufacturer,
hwVersion, swVersion, authenticationScheme, token));
}
} catch (IllegalArgumentException e) {
throw new ConfigException(CONFIG_VALUE_ERROR, e);

View File

@ -10,6 +10,7 @@ COMPILE_DEPS = [
TEST_DEPS = [
'//lib:TEST_REST',
'//lib:minimal-json',
'//lib:jersey-security'
]
osgi_jar_with_tests (