added OAuth2 authentication for REST protocol
Change-Id: I3b8f3943ea043587730870a0b861760a4d6f3aa7
diff --git a/lib/BUCK b/lib/BUCK
index ccc1e15..5ff711f 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -548,6 +548,15 @@
)
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',
url = 'mvn:org.glassfish.jersey.core:jersey-common:jar:2.25.1',
diff --git a/lib/deps.json b/lib/deps.json
index 7fae453..f1ff1e9 100644
--- a/lib/deps.json
+++ b/lib/deps.json
@@ -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",
diff --git a/lib/pom.xml b/lib/pom.xml
index 12045a9..3398de1 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -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>
diff --git a/protocols/rest/api/BUCK b/protocols/rest/api/BUCK
index 74f33b2..471084f 100644
--- a/protocols/rest/api/BUCK
+++ b/protocols/rest/api/BUCK
@@ -3,6 +3,7 @@
'//utils/rest:onlab-rest',
'//lib:CORE_DEPS',
'//lib:jersey-client',
+ '//lib:jersey-security',
'//lib:jersey-common',
'//lib:httpclient-osgi',
'//lib:httpcore-osgi',
diff --git a/protocols/rest/api/pom.xml b/protocols/rest/api/pom.xml
index ff5f7de..e334e7a 100644
--- a/protocols/rest/api/pom.xml
+++ b/protocols/rest/api/pom.xml
@@ -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>
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
index 4a893fcb..602d989 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
@@ -25,10 +25,12 @@
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.Map;
import java.util.concurrent.ConcurrentHashMap;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* The implementation of HttpSBController.
*/
@@ -102,11 +106,7 @@
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 @@
}
}
- 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 @@
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];
}
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
index 8f59093..fef7f37 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
@@ -41,6 +41,8 @@
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 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 @@
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) ?
@@ -159,6 +163,16 @@
}
@Override
+ public AuthenticationScheme authentication() {
+ return authenticationScheme;
+ }
+
+ @Override
+ public String token() {
+ return token;
+ }
+
+ @Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
@@ -168,6 +182,8 @@
.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))
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
index edcce6e..6767b2d 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
@@ -26,6 +26,16 @@
*/
public interface RestSBDevice {
/**
+ * REST Authentication schemes.
+ */
+ public enum AuthenticationScheme {
+ NO_AUTHENTICATION,
+ BASIC,
+ OAUTH,
+ OAUTH2,
+ }
+
+ /**
* Returns the ip of this device.
*
* @return ip
@@ -40,6 +50,20 @@
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.
*
* @return username
@@ -91,6 +115,7 @@
/**
* 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 @@
* @return the software version.
*/
Optional<String> swVersion();
+
}
diff --git a/providers/rest/BUCK b/providers/rest/BUCK
index 81fe5b4..b6e2e2b 100644
--- a/providers/rest/BUCK
+++ b/providers/rest/BUCK
@@ -3,6 +3,7 @@
'//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',
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java
index 648e56a..cd290e5 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java
@@ -21,6 +21,7 @@
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 @@
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 @@
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)) {
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
index 091dc34..7f6d9e8 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
@@ -28,8 +28,6 @@
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.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.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.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 @@
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
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java
index e04be6f..21d9774 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java
@@ -21,15 +21,17 @@
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 @@
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 @@
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);
diff --git a/web/api/BUCK b/web/api/BUCK
index 69c1cbf..c1c6ab4 100644
--- a/web/api/BUCK
+++ b/web/api/BUCK
@@ -10,6 +10,7 @@
TEST_DEPS = [
'//lib:TEST_REST',
'//lib:minimal-json',
+ '//lib:jersey-security'
]
osgi_jar_with_tests (