Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hawk Authentication for sources #57

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/HTTP-batchsource.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,24 @@ is stopped.

**Refresh Token:** Token used to receive accessToken, which is end product of OAuth2.

### Hawk Authentication

**HAWK Authentication Enabled:** If true, plugin will perform HAWK authentication.

**HAWK Auth ID:** HAWK Authentication ID

**Hawk Auth Key:** HAWK Authentication Key

**Algorithm:** Hash Algorithm used

**ext:** Any application-specific information to be sent with the request. Ex: some-app-extra-data

**app:** This provides binding between the credentials and the application in a way that prevents an attacker from ticking an application to use credentials issued to someone else.

**dlg:** The application id of the application the credentials were directly issued to.

**Include Payload Hash:** HAWK authentication provides optional support for payload validation. If this option is selected, the payload hash will be calculated and included in MAC calculation and in Authorization header

### SSL/TLS

**Verify HTTPS Trust Certificates:** If false, untrusted trust certificates (e.g. self signed), will not lead to an
Expand Down
18 changes: 18 additions & 0 deletions docs/HTTP-streamingsource.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,24 @@ is stopped.

**Refresh Token:** Token used to receive accessToken, which is end product of OAuth2.

### Hawk Authentication

**HAWK Authentication Enabled:** If true, plugin will perform HAWK authentication.

**HAWK Auth ID:** HAWK Authentication ID

**Hawk Auth Key:** HAWK Authentication Key

**Algorithm:** Hash Algorithm used

**ext:** Any application-specific information to be sent with the request. Ex: some-app-extra-data

**app:** This provides binding between the credentials and the application in a way that prevents an attacker from ticking an application to use credentials issued to someone else.

**dlg:** The application id of the application the credentials were directly issued to.

**Include Payload Hash:** HAWK authentication provides optional support for payload validation. If this option is selected, the payload hash will be calculated and included in MAC calculation and in Authorization header

### SSL/TLS

**Verify HTTPS Trust Certificates:** If false, untrusted trust certificates (e.g. self signed), will not lead to an
Expand Down
8 changes: 6 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<gson.version>2.8.5</gson.version>
<hadoop.version>2.3.0</hadoop.version>
<httpcomponents.version>4.5.9</httpcomponents.version>
<hydrator.version>2.4.0-SNAPSHOT</hydrator.version>
<hydrator.version>2.6.0</hydrator.version>
<jackson.version>2.9.9</jackson.version>
<junit.version>4.11</junit.version>
<jython.version>2.7.1</jython.version>
Expand Down Expand Up @@ -354,7 +354,11 @@
<artifactId>jython-standalone</artifactId>
<version>${jython.version}</version>
</dependency>

<dependency>
<groupId>com.wealdtech.hawk</groupId>
<artifactId>hawk-core</artifactId>
<version>1.0.0</version>
</dependency>

<!-- tests -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.cdap.plugin.http.source.common;

import com.google.common.base.Strings;
import com.wealdtech.hawk.HawkCredentials;
import io.cdap.cdap.api.annotation.Description;
import io.cdap.cdap.api.annotation.Macro;
import io.cdap.cdap.api.annotation.Name;
Expand Down Expand Up @@ -87,6 +88,14 @@ public abstract class BaseHttpSourceConfig extends ReferencePluginConfig {
public static final String PROPERTY_CLIENT_SECRET = "clientSecret";
public static final String PROPERTY_SCOPES = "scopes";
public static final String PROPERTY_REFRESH_TOKEN = "refreshToken";
public static final String PROPERTY_HAWK_AUTH_ENABLED = "hawkAuthEnabled";
public static final String PROPERTY_HAWK_AUTH_ID = "hawkAuthID";
public static final String PROPERTY_HAWK_AUTH_KEY = "hawkAuthKey";
public static final String PROPERTY_HAWK_AUTH_ALGORITHM = "hawkAlgorithm";
public static final String PROPERTY_HAWK_AUTH_EXT = "hawkExt";
public static final String PROPERTY_HAWK_AUTH_APP = "hawkApp";
public static final String PROPERTY_HAWK_AUTH_DLG = "hawkDlg";
public static final String PROPERTY_HAWK_PAYLOAD_HASH_ENABLED = "hawkPayloadHashEnabled";
public static final String PROPERTY_VERIFY_HTTPS = "verifyHttps";
public static final String PROPERTY_KEYSTORE_FILE = "keystoreFile";
public static final String PROPERTY_KEYSTORE_TYPE = "keystoreType";
Expand Down Expand Up @@ -316,6 +325,56 @@ public abstract class BaseHttpSourceConfig extends ReferencePluginConfig {
@Macro
protected String refreshToken;

@Name(PROPERTY_HAWK_AUTH_ENABLED)
@Description("If true, plugin will perform OAuth2 authentication.")
protected String hawkAuthEnabled;

@Nullable
@Name(PROPERTY_HAWK_AUTH_ID)
@Description("The HAWK Authentication ID")
@Macro
protected String hawkAuthID;

@Nullable
@Name(PROPERTY_HAWK_AUTH_KEY)
@Description("The HAWK Authentication Key")
@Macro
protected String hawkAuthKey;

@Nullable
@Name(PROPERTY_HAWK_AUTH_ALGORITHM)
@Description("The HAWK Algorithm")
@Macro
protected String hawkAlgorithm;

@Nullable
@Name(PROPERTY_HAWK_AUTH_EXT)
@Description("Advanced parameter : Any application-specific information to be sent with the request. " +
"Ex: some-app-extra-data")
@Macro
protected String hawkExt;

@Nullable
@Name(PROPERTY_HAWK_AUTH_APP)
@Description("Advanced parameter : This provides binding between the credentials and the application " +
"in a way that prevents an attacker from ticking an application to use credentials issued to someone else.")
@Macro
protected String hawkApp;

@Nullable
@Name(PROPERTY_HAWK_AUTH_DLG)
@Description("Advanced parameter : The application id of the application the credentials were directly issued to.")
@Macro
protected String hawkDlg;

@Nullable
@Name(PROPERTY_HAWK_PAYLOAD_HASH_ENABLED)
@Description("Advanced parameter : HAWK authentication provides optional support for payload validation. " +
"If this option is selected, the payload hash will be calculated and included in MAC calculation " +
"and in Authorization header")
@Macro
protected String hawkPayloadHashEnabled;

@Name(PROPERTY_VERIFY_HTTPS)
@Description("If false, untrusted trust certificates (e.g. self signed), will not lead to an" +
"error. Do not disable this in production environment on a network you do not entirely trust. " +
Expand Down Expand Up @@ -563,6 +622,46 @@ public String getRefreshToken() {
return refreshToken;
}


public boolean getHawkAuthEnabled() {
return Boolean.parseBoolean(hawkAuthEnabled);
}

@Nullable
public String getHawkAuthID() {
return hawkAuthID;
}

@Nullable
public String getHawkAuthKey() {
return hawkAuthKey;
}

@Nullable
public HawkCredentials.Algorithm getHawkAlgorithm() {
return HawkCredentials.Algorithm.parse(hawkAlgorithm);
}

@Nullable
public String getHawkExt() {
return hawkExt;
}

@Nullable
public String getHawkApp() {
return hawkApp;
}

@Nullable
public String getHawkDlg() {
return hawkDlg;
}

@Nullable
public boolean getHawkPayloadHashEnabled() {
return Boolean.parseBoolean(hawkPayloadHashEnabled);
}

public Boolean getVerifyHttps() {
return Boolean.parseBoolean(verifyHttps);
}
Expand Down Expand Up @@ -794,6 +893,14 @@ PAGINATION_INDEX_PLACEHOLDER, getPaginationType()),
assertIsSet(getRefreshToken(), PROPERTY_REFRESH_TOKEN, reasonOauth2);
}

// Validate HAWK auth properties
if (!containsMacro(PROPERTY_HAWK_AUTH_ENABLED) && this.getHawkAuthEnabled()) {
String reasonHAWK = "HAWK Authentication is enabled";
assertIsSet(getHawkAuthID(), PROPERTY_HAWK_AUTH_ID, reasonHAWK);
assertIsSet(getHawkAuthKey(), PROPERTY_HAWK_AUTH_KEY, reasonHAWK);
assertIsSet(getHawkAlgorithm(), PROPERTY_HAWK_AUTH_ALGORITHM, reasonHAWK);
}

if (!containsMacro(PROPERTY_VERIFY_HTTPS) && !getVerifyHttps()) {
assertIsNotSet(getTrustStoreFile(), PROPERTY_TRUSTSTORE_FILE,
String.format("trustore settings are ignored due to disabled %s", PROPERTY_VERIFY_HTTPS));
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/io/cdap/plugin/http/source/common/http/HawkUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright © 2019 Cask Data, Inc.
*
* 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 io.cdap.plugin.http.source.common.http;

import com.wealdtech.hawk.HawkClient;
import com.wealdtech.hawk.HawkCredentials;
import org.apache.http.entity.StringEntity;

import java.net.URI;

/**
* A class which contains utilities to make HAWK specific calls.
*/
public class HawkUtil {

public static HawkClient createHawkClient(String authID, String authKey, HawkCredentials.Algorithm algorithm) {
HawkCredentials hawkCredentials = new HawkCredentials.Builder()
.keyId(authID)
.key(authKey)
.algorithm(algorithm)
.build();

return new HawkClient.Builder().credentials(hawkCredentials).build();
}

public static String getAuthorizationHeader(
HawkClient hawkClient,
StringEntity requestBody,
URI uri,
String method,
boolean payloadHashEnabled,
String ext,
String app,
String dlg) {
String hash = null;
if (payloadHashEnabled) {
hash = Integer.toString(requestBody.hashCode());
}

return hawkClient.generateAuthorizationHeader(
uri,
method,
hash,
ext,
app,
dlg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.wealdtech.hawk.HawkClient;
import io.cdap.plugin.http.source.common.BaseHttpSourceConfig;
import org.apache.http.Header;
import org.apache.http.HttpHost;
Expand Down Expand Up @@ -48,6 +49,7 @@ public class HttpClient implements Closeable {
private final BaseHttpSourceConfig config;
private final StringEntity requestBody;
private CloseableHttpClient httpClient;
private HawkClient hawkClient;

public HttpClient(BaseHttpSourceConfig config) {
this.config = config;
Expand All @@ -65,22 +67,45 @@ public HttpClient(BaseHttpSourceConfig config) {
* Executes HTTP request with parameters configured in plugin config and returns response.
* Is called to load every page by pagination iterator.
*
* @param uri URI of resource
* @param uriStr URI of resource
* @return a response object
* @throws IOException in case of a problem or the connection was aborted
*/
public CloseableHttpResponse executeHTTP(String uri) throws IOException {
public CloseableHttpResponse executeHTTP(String uriStr) throws IOException {
// lazy init. So we are able to initialize the class for different checks during validations etc.
if (httpClient == null) {
httpClient = createHttpClient();
}

HttpEntityEnclosingRequestBase request = new HttpRequest(URI.create(uri), config.getHttpMethod());
URI uri = URI.create(uriStr);
HttpEntityEnclosingRequestBase request = new HttpRequest(uri, config.getHttpMethod());

if (requestBody != null) {
request.setEntity(requestBody);
}

if (config.getHawkAuthEnabled()) {
if (hawkClient == null) {
hawkClient = HawkUtil.createHawkClient(
config.getHawkAuthID(),
config.getHawkAuthKey(),
config.getHawkAlgorithm()
);
}

String authorizationHeader = HawkUtil.getAuthorizationHeader(
hawkClient,
requestBody,
uri,
request.getMethod(),
config.getHawkPayloadHashEnabled(),
config.getHawkExt(),
config.getHawkApp(),
config.getHawkDlg()

);
request.addHeader("Authorization", authorizationHeader);
}

return httpClient.execute(request);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ protected Map<String, String> getProperties(Map<String, String> sourceProperties
.put("referenceName", testName.getMethodName())
.put(BaseHttpSourceConfig.PROPERTY_HTTP_METHOD, "GET")
.put(BaseHttpSourceConfig.PROPERTY_OAUTH2_ENABLED, "false")
.put(BaseHttpSourceConfig.PROPERTY_HAWK_AUTH_ENABLED, "false")
.put(BaseHttpSourceConfig.PROPERTY_HTTP_ERROR_HANDLING, "2..:Success,.*:Fail")
.put(BaseHttpSourceConfig.PROPERTY_ERROR_HANDLING, "stopOnError")
.put(BaseHttpSourceConfig.PROPERTY_RETRY_POLICY, "linear")
Expand Down
Loading