OAuth 2.0 improves the overall security of the scenario by avoiding to pass the user's confidential username and password to the OAuth client. Using OAuth, the user only shares their credentials with the identity provider (IdP). Although the OAuth client still has to take care for secure storage of the issued access token in step 6, the potential damage of a stolen token compared to a stolen password is considerably lower. The access token only authorizes an OAuth client to call a single API endpoint on behalf of the user. A username and password has a much broader scope - it may allow access to a large number of web sites and services.
package org.openhab.binding.authzcode;
...
public class WebViewServlet extends HttpServlet {
private static final String SERVLET_NAME = "/webview";
....
/**
* Activates the webview servlet.
*/
protected void activate() {
try {
logger.debug("Starting up authzcode webview servlet at " + SERVLET_NAME);
Hashtable<String, String> props = new Hashtable<String, String>();
httpService.registerServlet(SERVLET_NAME, this, props, createHttpContext());
bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
if (bundleContext != null) {
ServiceReference<?> serviceReference = bundleContext.getServiceReference(AuthzCodeBindingProvider.class.getName());
if (serviceReference != null) {
bindingprovider = (AuthzCodeBindingProvider) bundleContext.getService(serviceReference);
} else
logger.error("BindungProvider is null");
// get all items for this binding
for (String itemName : bindingprovider.getItemNames()) {
Item sapHcpItem = itemUIRegistry.getItem(itemName);
logger.debug("Found item: " + sapHcpItem.getName());
}
} else
logger.error("bundleContext is null");
} catch (Exception ex) {
logger.error("Error during saphcp-webview servlet startup", ex);
}
}
/**
* Deactivates the webview servlet.
*/
protected void deactivate() {
httpService.unregister(SERVLET_NAME);
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
logger.debug("Received WebView Request");
String action = request.getParameter("action");
String request_clientid = request.getParameter("clientid");
if (StringUtils.isNotBlank(request_clientid)) {
this.clientid = request_clientid;
}
logger.debug("clientid set to: " + clientid);
if (StringUtils.isNotBlank(action) && action.equalsIgnoreCase("sendcode")) {
logger.debug("Executing action: " + action);
String code = request.getParameter("code");
// create command (string)
String commandString = "authorize " + clientid + " " + code;
StringType command = new StringType(commandString);
if (bindingprovider.getItemNames() != null) {
// send command synchronously
String firstItemName = bindingprovider.getItemNames().iterator().next();
eventPublisher.sendCommand(firstItemName, command);
} else {
logger.warn("No item with authzcode binding found");
}
}
response.getWriter().println(buildFormString());
}
private String buildFormString() {
String result = "<html>";
result += "<head><style>p {font-family:verdana;}</style></head>";
result += "<body><form action='./webview' method='get'>";
result += "<p>Code <input type='text' name='code'>";
result += "<input type='hidden' name='action' value='sendcode'>";
result += "<input type='submit' value='Submit'></p></form>";
// update state
// check if file with token exists
String accessToken;
accessToken = AuthzCodeCommons.loadToken(this.clientid);
if (StringUtils.isBlank(accessToken)) {
result += buildAccessTokenFailed();
} else {
result += buildAuthorizedString();
}
result += "</body></html>";
return result;
}
private String buildAuthorizedString() {
return "<p><span style=\"color:green\">Sensor is authorized!</span></p>";
}
private String buildAccessTokenFailed() {
return "<p><span style=\"color:red\">Sensor is not authorized!</span></p>";
}
}
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="activate" deactivate="deactivate" name="org.openhab.authzcode">
<implementation class="org.openhab.binding.authzcode.WebViewServlet"/>
<reference bind="setHttpService" cardinality="1..1" interface="org.osgi.service.http.HttpService" name="HttpService" policy="dynamic" unbind="unsetHttpService"/>
<reference bind="setItemUIRegistry" cardinality="1..1" interface="org.openhab.ui.items.ItemUIRegistry" name="ItemUIRegistry" policy="dynamic" unbind="unsetItemUIRegistry"/>
<reference bind="setEventPublisher" cardinality="1..1"
interface="org.openhab.core.events.EventPublisher" name="EventPublisher"
policy="dynamic" unbind="unsetEventPublisher" />
</scr:component>
Service-Component: OSGI-INF/binding.xml, OSGI-INF/genericbindingprovider.xml, OSGI-INF/webviewservlet.xml
public class AuthzCodeBinding extends AbstractBinding<AuthzCodeBindingProvider> implements ManagedService {
...
@Override
protected void internalReceiveCommand(String itemName, Command command) {
// the code being executed when a command was sent on the openHAB
// event bus goes here. This method is only called if one of the
// BindingProviders provide a binding for the given 'itemName'.
// process command
if (command instanceof StringType) {
StringType t = (StringType) command;
logger.debug("internalReceiveCommand() is called with command \"" + t + "\"");
String[] commandParts = StringUtils.split(t.toString());
if (commandParts[0].equals("authorize")) {
String clientId = commandParts[1];
String authzCode = commandParts[2];
this.authorize(clientId, authzCode);
}
}
}
private void authorize(String clientId, String authzCode) {
logger.debug("authorize() method is called with value " + authzCode);
HttpClient httpClient = new HttpClient();
if (this.proxyHost != null) {
httpClient.getHostConfiguration().setProxy(this.proxyHost, new Integer(this.proxyPort).intValue());
}
PostMethod httpPost = new PostMethod(this.tokenendpoint);
httpPost.addRequestHeader("content-type", "application/x-www-form-urlencoded");
httpPost.addParameter("client_id", clientId);
httpPost.addParameter("grant_type", "authorization_code");
httpPost.addParameter("code", authzCode);
httpPost.addParameter("redirect_uri", this.redirectUri);
BufferedReader br = null;
try{
int returnCode = httpClient.executeMethod(httpPost);
if(returnCode != HttpStatus.SC_OK) {
// still consume the response body
String errorResponseBody = httpPost.getResponseBodyAsString();
logger.error("Failed to request access Token" + errorResponseBody);
} else {
// Read the response body.
byte[] responseBody = httpPost.getResponseBody();
// parse response for token
Object accessTokenResponse = JSONValue.parse(new String(responseBody));
JSONObject array=(JSONObject)accessTokenResponse;
// store access token
AuthzCodeCommons.saveToken(clientId, (String)array.get("access_token"));
}
} catch (Exception e) {
logger.error("Failed to store access token for client " + clientId + ": " + e.getMessage());
} finally {
httpPost.releaseConnection();
if(br != null) try { br.close(); } catch (Exception fe) {}
}
}
...
...
String Authz_Thermostat_Kitchen {authzcode}
String Authz_Thermostat_Livingroom {authzcode}
String Authz_Thermostat_Bath {authzcode}
sitemap homezone label="Homezone"
{
Frame {
Text item=Thermostat_Livingroom_Actual {
Frame {
Setpoint item=Thermostat_Livingroom_Target step=0.5 maxValue=28
Webview item=Authz_Thermostat_Livingroom url="http://<openHAB host>:<port>/webview?clientid=sensor1" height=2
}
}
}
...
package org.openhab.action.saphcp.internal;
...
public class SapHCP {
...
public static boolean sendRequest(String sensorValue, String unit, String type, String description, String clientId) {
...
// check for file saphcp.token in etc directory
String accessToken = AuthzCodeCommons.loadToken(clientId);
if (StringUtils.isNotEmpty(accessToken)) {
// found the token
logger.debug("Found token for client with id " + clientId);
// post new temperature value to all HCP items
HttpClient httpClient = new HttpClient();
if (SapHCP.defaultProxyHost != null && SapHCP.defaultProxyPort != null) {
httpClient.getHostConfiguration().setProxy(SapHCP.defaultProxyHost, new Integer(SapHCP.defaultProxyPort).intValue());
}
PostMethod httpPost = new PostMethod(SapHCP.defaultAPIUrl);
// add access token as authorization header
httpPost.addRequestHeader("Authorization", "Bearer " + accessToken);
JSONObject newSensorValue=new JSONObject();
newSensorValue.put("value", sensorValue);
newSensorValue.put("sensorId", clientId);
newSensorValue.put("unit", unit);
newSensorValue.put("type", type);
newSensorValue.put("description", description);
try {
StringRequestEntity requestBody = new StringRequestEntity(newSensorValue.toJSONString(), "application/json", "UTF-8");
httpPost.setRequestEntity(requestBody);
BufferedReader br = null;
try
{
int returnCode = httpClient.executeMethod(httpPost);
// read response
if(returnCode != HttpStatus.SC_OK) {
// still consume the response body
String errorResponseBody = httpPost.getResponseBodyAsString();
logger.error("Failed to send new temperature value: " + errorResponseBody);
} else {
// Read the response body.
byte[] responseBody = httpPost.getResponseBody();
// parse response
logger.debug("New temperature value stored. ID: " + new String(responseBody));
}
} catch (Exception e) {
logger.error("Unknown Host Exception: " + e.getMessage());
} finally {
httpPost.releaseConnection();
if(br != null) try { br.close(); } catch (Exception fe) {}
}
} catch (UnsupportedEncodingException uee) {
logger.error("JSON encoding error: " + uee.getMessage());
}
} else {
logger.warn("No Access token found");
}
return true;
}
}
import org.openhab.core.library.types.*
import org.openhab.model.script.actions.*
import org.openhab.action.saphcp.*
rule "Send Living Room updates to HCP"
when
Item Thermostat_Livingroom_Actual received update
then
var value = Thermostat_Livingroom_Actual.state.toString
logInfo("sapHCP","Sensor 1: " + value)
sendRequest(value, "C", "Thermostat", "Living Room", "sensor1")
end
...
...
<filter>
<display-name>Sensor OAuth Protection</display-name>
<filter-name>PersistDataScopeFilter</filter-name>
<filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
<init-param>
<param-name>scope</param-name>
<param-value>persist-data</param-value>
</init-param>
<init-param>
<param-name>http-method</param-name>
<param-value>post</param-value>
</init-param>
<init-param>
<param-name>no-session</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>PersistDataScopeFilter</filter-name>
<url-pattern>/api/v1</url-pattern>
</filter-mapping>
...
@Path("measurement")
@Produces({ MediaType.APPLICATION_JSON })
public class MeasurementResource {
private static Logger logger = LoggerFactory.getLogger(MeasurementResource.class);
@GET
public Response getAllMeasurements()
{
logger.debug("getTemperatures() called");
MeasurementDAO measurementDAO = new MeasurementDAO();
List<Measurement> measurements = measurementDAO.getAllMeasurements();
return Response.ok().entity(measurements).build();
}
@GET
@Path("sensors")
public Response getSensors()
{
logger.debug("getSensors() called");
Collection<Measurement> resultList = new ArrayList<Measurement>();
MeasurementDAO measurementDAO = new MeasurementDAO();
List<String> sensorIDs = measurementDAO.getSensorIDs();
for (String sensorID : sensorIDs) {
Measurement lastMeasurementForSensor = measurementDAO.getLastMeasurementForSensor(sensorID);
resultList.add(lastMeasurementForSensor);
}
return Response.ok().entity(resultList).build();
}
@GET
@Path("sensor/{sensorId}")
public Response getMeasurementsForSensor(@PathParam("sensorId") String sensorId) {
MeasurementDAO measurementDAO = new MeasurementDAO();
List<Measurement> measurementsForSensor = measurementDAO.getSensorMeasurements(sensorId);
return Response.ok().entity(measurementsForSensor).build();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response addMeasurement(Measurement newMeasurement)
{
logger.debug("addTemperature() called");
MeasurementDAO measurementDAO = new MeasurementDAO();
long measurementId = measurementDAO.addMeasurement(newMeasurement);
return Response.ok().entity(measurementId).build();
}
}
<login-config>
<auth-method>FORM</auth-method>
</login-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Web UI Area</web-resource-name>
<url-pattern>/dashboard.html</url-pattern>
<url-pattern>/api/v1/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>Everyone</role-name>
</auth-constraint>
</security-constraint>
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
36 | |
25 | |
17 | |
13 | |
8 | |
7 | |
6 | |
6 | |
6 | |
6 |