Skip to content

Commit

Permalink
HAWKULAR-505 - Added API for Web Sockets authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
jpkrohling committed Sep 2, 2015
1 parent 9780e99 commit b101155
Show file tree
Hide file tree
Showing 40 changed files with 3,000 additions and 2 deletions.
10 changes: 10 additions & 0 deletions api/src/main/java/org/hawkular/accounts/api/PersonaService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.Set;

import org.hawkular.accounts.api.model.HawkularUser;
import org.hawkular.accounts.api.model.Persona;
import org.hawkular.accounts.api.model.Resource;
import org.hawkular.accounts.api.model.Role;
Expand Down Expand Up @@ -68,4 +69,13 @@ public interface PersonaService {
* @return the current persona.
*/
Persona getCurrent();

/**
* Checks if the current user is allowed to impersonate the given persona. At this moment, this implementation
* returns only "true", but will soon be changed to the appropriate behavior.
* @param actual the logged in user
* @param toImpersonate the persona to be impersonated
* @return whether or not the current user can impersona the given persona
*/
boolean isAllowedToImpersonate(HawkularUser actual, Persona toImpersonate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.hawkular.accounts.api.PersonaService;
import org.hawkular.accounts.api.UserService;
import org.hawkular.accounts.api.internal.adapter.HawkularAccounts;
import org.hawkular.accounts.api.model.HawkularUser;
import org.hawkular.accounts.api.model.Organization;
import org.hawkular.accounts.api.model.OrganizationMembership;
import org.hawkular.accounts.api.model.Persona;
Expand Down Expand Up @@ -158,11 +159,21 @@ public Persona getCurrent() {
// for now, this is sufficient. In a future improvement, we'll have a way to switch users
String personaId = httpRequest.getHeader("Hawkular-Persona");
if (personaId != null && !personaId.isEmpty()) {
// TODO: check permissions!
return get(personaId);
Persona persona = get(personaId);
if (isAllowedToImpersonate(userService.getCurrent(), persona)) {
return persona;
} else {
throw new RuntimeException("User is not allowed to impersonate this persona.");
}
}

// we don't have a persona on the request, so, assume it's the current user
return userService.getCurrent();
}

@Override
public boolean isAllowedToImpersonate(HawkularUser actual, Persona toImpersonate) {
// TODO: check the permissions, to see if the user indeed has the permissions to impersonate...
return true;
}
}
54 changes: 54 additions & 0 deletions common/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2015 Red Hat, Inc. and/or its affiliates
and other contributors as indicated by the @author tags.
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.hawkular.accounts</groupId>
<artifactId>hawkular-accounts-parent</artifactId>
<version>1.0.11.Final-SNAPSHOT</version>
</parent>

<name>Hawkular - Accounts - API</name>
<artifactId>hawkular-accounts-common</artifactId>
<packaging>jar</packaging>

<description>
Useful code for Accounts in general.
</description>

<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.json</groupId>
<artifactId>jboss-json-api_1.0_spec</artifactId>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.hawkular.accounts.common;

import java.io.StringReader;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.servlet.ServletContext;

/**
* @author Juraci Paixão Kröhling
*/
@ApplicationScoped
public class ApplicationResources {
private static final String REALM_CONFIG_KEY = "org.keycloak.json.adapterConfig";
private String realmConfiguration = null;
private ServletContext servletContext;

private boolean realmConfigurationParsed = false;

private String realmName;
private String serverUrl;
private String resourceName;
private String secret;

public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}

@Produces @RealmConfiguration
public String getRealmConfiguration() {
if (null == realmConfiguration) {
realmConfiguration = servletContext.getInitParameter(REALM_CONFIG_KEY);
}
return realmConfiguration;
}

@Produces @RealmName
public String getRealmName() {
if (!realmConfigurationParsed) {
parseRealmConfiguration();
}
return realmName;
}

@Produces @AuthServerUrl
public String getServerUrl() {
if (!realmConfigurationParsed) {
parseRealmConfiguration();
}
return serverUrl;
}

@Produces @RealmResourceName
public String getResourceName() {
if (!realmConfigurationParsed) {
parseRealmConfiguration();
}
return resourceName;
}

@Produces @RealmResourceSecret
public String getResourceNameSecret() {
if (!realmConfigurationParsed) {
parseRealmConfiguration();
}
return secret;
}

private void parseRealmConfiguration() {
JsonReader jsonReader = Json.createReader(new StringReader(getRealmConfiguration()));
JsonObject configurationJson = jsonReader.readObject();
JsonObject credentials = configurationJson.getJsonObject("credentials");

realmName = configurationJson.getString("realm");
serverUrl = configurationJson.getString("auth-server-url-for-backend-requests");
resourceName = configurationJson.getString("resource");
secret = credentials.getString("secret");

realmConfigurationParsed = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.hawkular.accounts.common;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Base64;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

/**
* @author Juraci Paixão Kröhling
*/
@ApplicationScoped
public class AuthServerRequestExecutor {
@Inject @RealmResourceName
private String clientId;

@Inject @RealmResourceSecret
private String secret;

public String execute(String url, String method) throws Exception {
return execute(url, null, clientId, secret, method);
}

public String execute(String url, String urlParameters, String method) throws Exception {
return execute(url, urlParameters, clientId, secret, method);
}

public String execute(String url, String clientId, String secret, String method) throws Exception {
return execute(url, null, clientId, secret, method);
}

/**
* Performs an HTTP call to the Keycloak server, returning the server's response as String.
* @param url the full URL to call, including protocol, host, port and path.
* @param urlParameters the HTTP Query Parameters properly encoded and without the leading "?".
* @param clientId the OAuth client ID.
* @param secret the OAuth client secret.
* @param method the HTTP method to use (GET or POST). If anything other than POST is sent, GET is used.
* @return a String with the response from the Keycloak server, in both success and error scenarios
* @throws Exception if communication problems with the Keycloak server occurs.
*/
public String execute(String url, String urlParameters, String clientId, String secret, String method) throws
Exception {

HttpURLConnection connection;
String credentials = clientId + ":" + secret;
String authorizationHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes());

if ("POST".equalsIgnoreCase(method)) {
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod(method);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Authorization", authorizationHeader);
connection.setDoInput(true);
connection.setDoOutput(true);

if (null != urlParameters) {
try (PrintWriter out = new PrintWriter(connection.getOutputStream())) {
out.print(urlParameters);
}
}
} else {
connection = (HttpURLConnection) new URL(url + "?" + urlParameters).openConnection();
connection.setRequestMethod(method);
connection.setRequestProperty("Authorization", authorizationHeader);
}

int timeout = Integer.parseInt(System.getProperty("org.hawkular.accounts.http.timeout", "5000"));
connection.setConnectTimeout(timeout);
connection.setReadTimeout(timeout);

StringBuilder response = new StringBuilder();

int statusCode;
try {
statusCode = connection.getResponseCode();
} catch (SocketTimeoutException timeoutException) {
throw new UsernamePasswordConversionException("Timed out when trying to contact the Keycloak server.");
}

InputStream inputStream;
if (statusCode < 300) {
inputStream = connection.getInputStream();
} else {
inputStream = connection.getErrorStream();
}

try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
for (String line; (line = reader.readLine()) != null;) {
response.append(line);
}
}

return response.toString();

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.hawkular.accounts.common;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

/**
* @author Juraci Paixão Kröhling
*/
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, PARAMETER, METHOD})
public @interface AuthServerUrl {
}

0 comments on commit b101155

Please sign in to comment.