I'm trying to retrieve secrets from vault using the AppRole authentication. But I get the error :
java.lang.IllegalArgumentException: URI is not absolute
What I do is create a vaultEndpoint then depending on the method choosen I use token authentication or AppRole authentication. There's no issue with the token authentication, however whenever I try to retrive a secret or even get the vaultToken to login with AppRole the URI is not absolute error occurs.
I 've seen in https://docs.oracle.com/javase/8/docs/api/java/net/URI.html that an URI is absolute when it specifies a scheme otherwise it is relative. But I think that my URI is specifing a scheme.
So I'm a bit lost here.
Does anyone know what I am doing wrong ? Or why I get this error ?
I use spring-vault-core-2.2.0.RELEASE
Here's my code :
VaultEndpoint ep = VaultEndpoint.create(host, portInt);
if (scheme != null) {
ep.setScheme(scheme);
}
if (authMethod.equals("token")) {
vaultTemplate = new VaultTemplate(ep, new TokenAuthentication(token));
} else if (authMethod.equals("appRole")) {
RestOperations restOperations = VaultClients.createRestTemplate();
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId))
.secretId(AppRoleAuthenticationOptions.SecretId.wrapped(VaultToken.of(secretId))).build();
vaultTemplate = new VaultTemplate(ep, new AppRoleAuthentication(options, restOperations));
}
}
I have the same error if I try to get the vaultToken :
RestOperations restOperations = VaultClients.createRestTemplate();
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId))
.secretId(AppRoleAuthenticationOptions.SecretId.wrapped(VaultToken.of(uncryptedSecretId))).build();
AppRoleAuthentication appRoleAuth = new AppRoleAuthentication(options, restOperations);
VaultToken appRoleToken = appRoleAuth.login();
Here is the error :
java.lang.IllegalArgumentException: URI is not absolute
at java.net.URI.toURL(Unknown Source)
at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:98)
at org.springframework.vault.client.VaultClients.lambda$createRestTemplate$0(VaultClients.java:128)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:742)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:586)
at org.springframework.vault.authentication.AppRoleAuthentication.getSecretId(AppRoleAuthentication.java:305)
at org.springframework.vault.authentication.AppRoleAuthentication.getAppRoleLoginBody(AppRoleAuthentication.java:344)
at org.springframework.vault.authentication.AppRoleAuthentication.createTokenUsingAppRole(AppRoleAuthentication.java:201)
at org.springframework.vault.authentication.AppRoleAuthentication.login(AppRoleAuthentication.java:191)
After further investigation, the issue was how I instanciated the restTemplate.
I added the spring context library to my project and implemented the AbstractVaultConfiguration class. This class contains a restOperations() function that solved my problem.
This is how I solved the issue :
public class AppRoleAuthenticationService extends AbstractVaultConfiguration {
private String roleId;
private String secretId;
private String host;
private String scheme;
private String port;
public AppRoleAuthenticationService(String roleId, String secretId, String host, String scheme, String port) {
this.roleId = roleId;
this.secretId = secretId;
this.host = host;
this.scheme = scheme;
this.port = port;
}
#Override
public VaultEndpoint vaultEndpoint() {
int portInt = Integer.parseInt(port);
VaultEndpoint ep = VaultEndpoint.create(host, portInt);
if (scheme != null) {
ep.setScheme(scheme);
}
return ep;
}
#Override
public ClientAuthentication clientAuthentication() {
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(AppRoleAuthenticationOptions.RoleId.provided(roleId))
.secretId(AppRoleAuthenticationOptions.SecretId.provided(secretId)).build();
return new AppRoleAuthentication(options, restOperations());
}
}
And then just use this class :
AppRoleAuthenticationService appRoleAuth = new AppRoleAuthenticationService(roleId,
uncryptedSecretId, host, scheme, port);
VaultEndpoint vaultEp = appRoleAuth.vaultEndpoint();
ClientAuthentication auth = appRoleAuth.clientAuthentication();
vaultTemplate = new VaultTemplate(vaultEp, auth);
Related
I would like to retrieve all devices managed by Intune (managed devices) using the Microsoft Graph Java SDK. I have created the app in Microsoft Azure and given the appropriate API permissions:
API Permissions
The following code creates a graphClient object and a method that retrieves all managed devices.
#Service
public class AzureServiceDefault implements AzureService
{
private static final String CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXXX";
private static final List<String> SCOPES = Arrays.asList(new String[]{"https://graph.microsoft.com/.default"});
private static final String TENANT = "XXXXXXXXXXXXXXXXXXXXXXXX";
private static final String CLIENT_SECRET = "XXXXXXXXXXXXXXXXXXXXXXXX";
ClientCredentialProvider authProvider = new ClientCredentialProvider(CLIENT_ID, SCOPES, CLIENT_SECRET, TENANT, NationalCloud.Global);
IGraphServiceClient graphClient;
public AzureServiceDefault()
{
graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
}
#Override
public List<IntuneDevice> getManagedDevices()
{
IManagedDeviceCollectionRequestBuilder managedDeviceRequestBuilder;
IDeviceManagementRequestBuilder builder = graphClient.deviceManagement();
IDeviceManagementRequest managedDevicesRequest = builder.buildRequest();
List<ManagedDevice> managedDevices = new ArrayList<>();
List<IntuneDevice> allManagedDevices = new ArrayList<>();
do {
try {
DeviceManagement deviceManagement = managedDevicesRequest.get();
ManagedDeviceCollectionPage managedDevicesCollectionPage = deviceManagement.managedDevices;
//Process items in the response
managedDevices.addAll(managedDevicesCollectionPage.getCurrentPage());
managedDevices.stream().forEach((device) -> allManagedDevices.add(new IntuneDevice(device.id,
device.userId,
device.deviceName,
device.managedDeviceOwnerType.toString(),
device.operatingSystem,
device.osVersion,
device.complianceState.toString(),
device.azureADRegistered,
device.azureADDeviceId,
device.userPrincipalName,
device.model,
device.manufacturer,
device.serialNumber)));
//Build the request for the next page, if there is one
managedDeviceRequestBuilder = managedDevicesCollectionPage.getNextPage();
if (managedDeviceRequestBuilder == null)
{
managedDevicesRequest = null;
}
else
{
managedDevicesRequest = (IDeviceManagementRequest) managedDeviceRequestBuilder.buildRequest();
}
}
catch(ClientException ex)
{
ex.printStackTrace();
managedDevicesRequest = null;
}
} while (managedDevicesRequest != null);
return allManagedDevices;
}
}
The problem is that the variable managedDevices turns out to be null and this is the error message:
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "com.microsoft.graph.requests.extensions.ManagedDeviceCollectionPage.getCurrentPage()" because "managedDevicesCollectionPage" is null] with root cause
java.lang.NullPointerException: Cannot invoke "com.microsoft.graph.requests.extensions.ManagedDeviceCollectionPage.getCurrentPage()" because "managedDevicesCollectionPage" is null
What do I need to change to make this code work? I am succesfully able to retrieve all users in Azure AD, but I am having difficulties getting data from Intune/Endpoint Manager. Do I need to make changes to the SCOPES?
It should be possible to retrieve all managed devices as the REST API for it is https://graph.microsoft.com/v1.0/deviceManagement/managedDevices
Thanks for your help
This MS Graph API does not support application permissions, so you couldn't list managedDevices with ClientCredentialProvider. ClientCredentialProvider is based on client credential flow that requires application permission.
You could use AuthorizationCodeProvider to get the list. And follow this to get AUTHORIZATION_CODE first.
String CLIENT_ID = "xxxxxx";
List<String> SCOPES = Arrays.asList(new String[] { "https://graph.microsoft.com/.default" });
String CLIENT_SECRET = "xxxxxx";
String TENANT = "xxxxxx";
String AUTHORIZATION_CODE = "";
String REDIRECT_URL = "xxxxxx";
AuthorizationCodeProvider authProvider = new AuthorizationCodeProvider(CLIENT_ID, SCOPES, AUTHORIZATION_CODE,
REDIRECT_URL, NationalCloud.Global, TENANT, CLIENT_SECRET);
IGraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
IManagedDeviceCollectionPage managedDeviceCollectionPage = graphClient.deviceManagement().managedDevices().buildRequest().get();
List<ManagedDevice> managedDeviceList = managedDeviceCollectionPage.getCurrentPage();
I have a fairly simple case where I am trying to add HTTP headers (not SOAP headers) to a request I am making using Spring's WebServiceTemplate.
I have defined a ClientInterceptor where I am doing:
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
try {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection = (HttpComponentsConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", someAccessToken));
} catch (IOException exception) {
// Do nothing
}
return true;
}
This is how I configure my SomeClient which extends WebServiceConfigurationSupport:
#Bean
public SomeClient someClient() {
...
SomeClientImpl service = new SomeClientImpl();
service.setObjectFactory(new com.path.ObjectFactory());
service.setDefaultUri(someUri);
service.setMarshaller(marshaller);
service.setUnmarshaller(marshaller);
service.setxStreamMarshaller(xStreamMarshaller);
service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor()});
service.setMessageSender(new HttpComponentsMessageSender());
service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor(), addHttpHeaderInterceptor()});
return service;
}
#Bean
public ClientInterceptor addHttpHeaderInterceptor() {
return new AddHttpHeaderInterceptor(someAccessToken);
}
#Bean
public Wss4jSecurityInterceptor wss4jSecurityInterceptor() {
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
interceptor.setSecurementActions(securementAction);
interceptor.setSecurementUsername(securementUsername);
interceptor.setSecurementPassword(securementPassword);
interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
interceptor.setSecurementMustUnderstand(false);
return interceptor;
}
But the Authorization header is not being added. I have also tried with a CustomMessageCallback:
public class CustomMessageCallback implements WebServiceMessageCallback {
private String headerKey;
private String headerValue;
public CustomMessageCallback(String headerKey, String headerValue) {
this.headerKey = headerKey;
this.headerValue = headerValue;
}
#Override
public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection conn = (HttpComponentsConnection) context.getConnection();
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
}
But it does not seem to work as well. What am I doing wrong, why the Authorization header is not being added? Thanks!
Use the HeadersAwareSenderWebServiceConnection interface instead of the actual underlying connection.
TransportContext context = TransportContextHolder.getTransportContext();
HeadersAwareSenderWebServiceConnection connection = (HeadersAwareSenderWebServiceConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", "********"));
Now if you upgrade/switch HTTP library you don't need to change this code.
To answer your question about what you are doing wrong is that you are casting to the wrong class. Yes the class you are using is deprecated but it is part of the library you are using, you cannot just cast to a different class without changing the underlying HTTP library.
What I did in past is to use a WebServiceMessageCallback like this one:
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
private String headerKey;
private String headerValue;
private String soapAction;
public WsHttpHeaderCallback(String headerKey, String headerValue, String soapAction)
{
super();
this.headerKey = headerKey;
this.headerValue = headerValue;
this.soapAction = soapAction;
validateRequiredFields();
}
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
validateRequiredFields();
addRequestHeader(headerKey, headerValue);
if (StringUtils.hasText(this.soapAction))
{
AxiomSoapMessage axiomMessage = (AxiomSoapMessage) message;
axiomMessage.setSoapAction(this.soapAction);
}
}
private void validateRequiredFields()
{
if( !StringUtils.hasText(headerKey) )
{
throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con chiave non valida: ["+headerKey+"]");
}
if( !StringUtils.hasText(headerValue) )
{
throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con valore non valido: ["+headerValue+"]");
}
}
private void addRequestHeader(String headerKey, String headerValue)
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpComponentsConnection)
{
HttpComponentsConnection conn = (HttpComponentsConnection) connection;
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
else if( connection instanceof ClientHttpRequestConnection )
{
ClientHttpRequestConnection conn = (ClientHttpRequestConnection)connection;
conn.getClientHttpRequest().getHeaders().add(headerKey, headerValue);
}
}
}
Then I used it in this way:
wsTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, soapAction) );
In this way I successfully set all the needed HttpHeaders (in my case just one :) )
I hope it is usefull
Angelo
TL;DR
Your messageSender should be an instance of HttpComponentsMessageSender instead of HttpUrlConnectionMessageSender. Also you need to provide proper credentials.
getConnection() function of TransportContext returns an implementation of WebServiceConnection. Both HttpUrlConnection and HttpComponentsConnection are implementations of the same. So basically you are getting the wrong type of connection,hence the ClassCastException.
The ClientInterceptor will work for custom headers but not for Authorization header. For that, your HttpComponentsMessageSender needs to be configured with your credentials.
The proper configuration should be like this
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean
public SomeClient someClient() {
SomeClientImpl service = new SomeClientImpl();
service.setMessageSender();
//other configs
return service;
}
public HttpComponentsMessageSender getMessageSender(){
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
httpComponentsMessageSender.setCredentials(getCredentials);
}
public UsernamePasswordCredentials getCredentials(){
return new UsernamePasswordCredentials(username, password);
}
I went through a similar exercise, for an endpointInterceptor the connection returns a HttpServletConnection. Therefore used the following and managed to get the HTTP headers added.
HttpServletConnection connection = (HttpServletConnection)context.getConnection();
HttpServletResponse response = connection.getHttpServletResponse();
HttpServletRequest request = connection.getHttpServletRequest();
response.addHeader("myheader", "myvalue");
Some additional tips:
If you want to send back the same header you received in the request, use following in the handleResponse method of the endpointInterceptor
response.addHeader("myheader", request.getHeader("myheader"));
If you are trying to add custom headers in an clientInterceptor to send to a downstream use below in the handleRequest method,
HttpUrlConnection connection = (HttpUrlConnection)context.getConnection();
connection.addRequestHeader("myheader", "myvalue");
I am working on a client server application. I was using Restlet 2.0.3. Due to a heavy load task my client was getting timed-out. I searched on the forum and found that switching over to Restlet 2.2 would help. So I did that. I upgraded my Restlet to 2.2.1. But now my code has stopped working at precisely this method.
public synchronized UUID generateUniqueSessionId(String userAtDomain)
{
UUID newSessionId = UUID.randomUUID();
SessionAttributes sessionAttributes = new SessionAttributes();
sessionAttributes.setAlive(true);
sessionAttributes.setFQUserName(userAtDomain);
loggedInUsers.put(newSessionId, sessionAttributes);
return newSessionId;
}
So I am returning the UUID at last.
This code is on the server and invoked during login. Following is the error that I am getting from the logs.
16 Mar 2015 11:23:18 WARN - Unable to find a converter for this object : f3d2edda-443c-454d-856a-fb4e7ed9c535
And this object referred in the log belongs to java.util.UUID
The code on the client side which invokes the server looks like this.
public UUID authenticateUser(String username, String passwd) {
try {
String url = RESTLetWebSvcsFactory.getFactoryInstance().getServer_URL() + "login/" + username + "/" + passwd;
Context context = new Context();
Client client = new Client(context, Protocol.HTTP);
ClientHelper helper = new ClientHelper(client);
helper.getHelpedParameters().set("socketConnectTimeoutMs", "60000");
ClientResource cr = new ClientResource(url);
LoginLogoutResource resource = cr.wrap(LoginLogoutResource.class);
return resource.loginUser();
} catch (ResourceException re) {
if (re.getStatus().isConnectorError()) {
try {
RESTLetWebSvcsFactory.enableFallBackServer();
String url = RESTLetWebSvcsFactory.getFactoryInstance().getServer_URL() + "login/" + username + "/" + passwd;
ClientResource cr = new ClientResource(url);
LoginLogoutResource resource = cr.wrap(LoginLogoutResource.class);
return resource.loginUser();
} catch (ResourceException re1) {
int statusCode = new RESTLetErrorHandler().handleServerError(re);
if (statusCode != -1) {
throw new UserCRUDException(statusCode);
}
}
} else {
throw new UserCRUDException(new RESTLetErrorHandler().handleServerError(re));
}
}
return null;
}
Note: USERCRUDException is my own exception and not one of JAVA
Please help me resolve this problem which probably prevents returning the UUID from the server and thus my application isn't moving ahead.
Thanks in advance
Restlet uses under the hood a generic bean conversion to convert bean to representation (and representation to bean). So your problem depends on the converter you use.
I made a test with the extension org.restlet.ext.jackson (that contains such bean converter) and with version 2.2.1 of Restlet. My test bean is described below:
public class TestBean {
private String name;
private String message;
private UUID uuid;
(...)
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
}
and the following test server resource:
public class MyServerResource extends ServerResource {
#Get
public TestBean ping() {
TestBean bean = new TestBean();
bean.setMessage("pong");
bean.setUuid(UUID.randomUUID());
return bean;
}
}
I received the following content for a GET method:
{
"name":"my name",
"message":"my message",
"uuid":"be9e5381-d5c9-4e45-b5c8-4af1f8bdca16"
}
Can you provide the converter you use? and the list of the Restlet dependencies you have in your application?
Hope it helps you,
Thierry
I am working on a new resource adapter for Glassfish.
It uses a connection pool that has a property set in the admin console.
Connector Connection Pools -> Additional Properties -> name=url, value=127.0.0.1
I would like to read this property from the resource adapter.
(from my managed connection implementation class for example)
I tried checking the documentation and online examples but did not find out how to do it.
This is the common way for almost all web apps on j2ee containers with connection pools.
InitialContext ctx = new InitialContext();
//The JDBC Data source that we just created
DataSource ds = (DataSource) ctx.lookup("url here");
Connection connection = ds.getConnection();
#Connector(reauthenticationSupport = false, transactionSupport = TransactionSupport.TransactionSupportLevel.NoTransaction)
public class SocketResourceAdapter implements ResourceAdapter {
/** The logger */
private static Logger log = Logger.getLogger("SocketResourceAdapter");
/** Name property */
#ConfigProperty(defaultValue = "DefaultMessage", supportsDynamicUpdates = true)
private String name;
#ConfigProperty(defaultValue = "---", type = String.class)
private String url;
#ConfigProperty(type = Integer.class)
private Integer port;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
And then I can just use getUrl() in the resource adapter.
At first it did not work because I was setting the properties for the connection factory and not the resource adapter.
I have a simple WSDL for a webservice. With wsimport I generated the java wrappers. And I have implemented the generated interface in a class "MyService". Then I publish this webservice with the built-in features of Java6:
MyService myService = new MyService();
Endpoint endpoint = Endpoint.publish("http://localhost:80/servicetest/services/MyServiceINSoap", myService);
When I let this code run, everything works perfect - I am able to connect to the service with a soapclient and can execute the methods.
Now I want the built-in webserver of the javavm to use basic authentication - so the client has to provide an userid and a password. How do I accomplish this? Is this possible? Or do I need an application server (tomcat, jboss, whatever...)?
I have searched for some time but only found solutions for webservice which are hosted in an application server.
Many thanks in advance!
erwrock
As I found your answer I was quite relieved because I searched hours for a solution. But as I tried to follow your link I noticed that the page is not available any longer. Fortunately, I found the old version with the help of the Wayback Machine (https://archive.org/web/). Although, this article is great I had some problems with getting the basic auth to work at the client side. In the end I got it working with the help of java.net.Authenticator. Here are the code snippets which I hope to be useful for others.
Server:
javax.xml.ws.Endpoint serverEndpoint = Endpoint.create(new MyService());
com.sun.net.httpserver.HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
com.sun.net.httpserver.HttpContext httpContext = server.createContext("/myservice");
server.start();
AuthenticatorServer authenticatorServer = new AuthenticatorServer("myRealm", "myName", "myPwd");
httpContext.setAuthenticator(authenticatorServer);
serverEndpoint.publish(httpContext);
Where AuthenticatorServer is:
private static class AuthenticatorServer extends com.sun.net.httpserver.BasicAuthenticator {
private final String user;
private final String pwd;
public AuthenticatorServer(String realm, String user, String pwd) {
super(realm);
this.user = user;
this.pwd = pwd;
}
#Override
public boolean checkCredentials(String userNameInput, String passwordInput) {
return StringUtils.equals(user, userNameInput) && StringUtils.equals(pwd, passwordInput);
}
}
And on the Client side:
AuthenticatorClient authenticatorClient = new AuthenticatorClient("myName", "myPwd");
Authenticator.setDefault(authenticatorClient);
javax.xml.namespace.QName qName = new QName(namespaceURI, WS_SERVICE_NAME);
java.net.URL wsdlAddress = new URL(namespaceURI + WS_SERVICE_NAME + "?wsdl");
MyService service =(Service.create(wsdlAddress, qName)).getPort(MyService.class);
Where AuthenticatorClient is:
private static class AuthenticatorClient extends java.net.Authenticator {
private final String user;
private final String pwd;
public AuthenticatorClient(String user, String pwd) {
this.user = user;
this.pwd = pwd;
}
#Override
public PasswordAuthentication getPasswordAuthentication() {
return (new PasswordAuthentication(user, pwd == null ? null : pwd.toCharArray()));
}
}
Found a solution here:
https://today.java.net/pub/a/today/2007/07/03/jax-ws-web-services-without-ee-containers.html
after hours of trial-and-error finally the right searchwords in google :)