Spring Boot LDAP - spring-boot

I am working on Spring Boot application in which I need to verify that is user belong to domain or not?
To check it I want to verify the username entry though ldap.
Below is code for creating LDAP Configuration:
#Bean
public LdapContextSource contextSource(String url, String port, String baseName, String uName, String password) {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(url + ":" + port));
contextSource.setBase(baseName);
contextSource.setUserDn(uName);
contextSource.setPassword(password);
contextSource.setReferral("follow");
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate(String url, String port, String baseName, String uName, String password) {
final LdapTemplate ldapTemplate = new LdapTemplate(contextSource(url, port, baseName, uName, password));
return ldapTemplate;
}
I am getting below exception
default task-101) Error/Exception occured while login nested exception is javax.naming.PartialResultException [Root exception is javax.naming.ServiceUnavailableException: DomainDnsZones.magna.global:389; socket closed]

In ldapTemplate method add statement ldapTemplate.setIgnorePartialResultException(true); for ignoring partial result exception.

The connection to LDAP is closed . You need to add the SSL certificate for establishing the connection and check if there are any firewall openings required .

Related

Not able to call SOAP API in WebServiceGatewaySupport by Spring WebServiceTemplate - Need help to fix this issue

I am trying to call SOAP API in Java Spring Boot using WebServiceGatewaySupport by Spring WebServiceTemplate
Config java class
public WebServiceTemplate createWebServiceTemplate(Jaxb2Marshaller marshaller, ClientInterceptor clientInterceptor) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
//SOAP URL
webServiceTemplate.setDefaultUri("http://host/Services.asmx");
//Auth ---It seems issue is here only????? need to check
webServiceTemplate.setMessageSender(new Authentication());
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
webServiceTemplate.setCheckConnectionForFault(true);
webServiceTemplate.setInterceptors((ClientInterceptor[]) Arrays.asList(createLoggingInterceptor()).toArray());
return webServiceTemplate;
}
SOAP Client Call
public class TicketClient extends WebServiceGatewaySupport {
public String getTicket(Ticket req) {
System.out.println("test inside webservice support1");
response = (AcquireTicketResponse) getWebServiceTemplate().marshalSendAndReceive(req);
Authentication Class
public class Authentication extends HttpUrlConnectionMessageSender {
#Override protected void prepareConnection(HttpURLConnection connection) {
String userpassword = username+":"+password+":"+domain;
String encoded =
Base64.getEncoder().withoutPadding().encodeToString(userpassword.getBytes(StandardCharsets.UTF_8));
connection.setRequestProperty("Authorization", "Basic "+encoded); connection.setRequestProperty("Content-Type", "application/xml"); super.prepareConnection(connection);
}
Not using Authetication class and add the above into
ClientInterceptor
public class SoapLoggingInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String username="test";
String password="test";
String domain = "#test";
String userpassword = username+":"+password+domain;
String encoded = Base64.getEncoder().withoutPadding().encodeToString(userpassword.getBytes(StandardCharsets.UTF_8));
messageContext.setProperty("Authorization", "Basic "+encoded);
messageContext.setProperty("Content-type", "XML");
Case -1 --->When I passed (user, pwd, domain and content-type) through messagesender, content type is taking but throwed "BAD REQUEST ERROR 400"....When i comment contenttype property, then it throwed "INTERNAL SERVER ERROR 500".
Case-2...when I passed (user, pwd, domain and content-type) through ClientInterceptor , always it throwed "INTERNAL SERVER ERROR 500"......It seems Authentication properties for the service are not going to API call.............................Please suggest some options
Both the cases, Authentication is not passing to service, if i comment,Authentication code (userid/pwd/domain) in both cases also...no efforts in output
After setting the user ID/pwd
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String username="test";
String password="test";
String domain = "#test";
String userpassword = username+":"+password+domain;
byte[] userpassword = (username+":"+password).getBytes(StandardCharsets.UTF_8);
String encoded = Base64.getEncoder().encodeToString(userpassword);
ByteArrayTransportOutputStream os = new
ByteArrayTransportOutputStream();
try {
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection conn = context.getConnection();
((HeadersAwareSenderWebServiceConnection) conn).addRequestHeader("Authorization", "Basic " + encoded);
} catch (IOException e) {
throw new WebServiceIOException(e.getMessage(), e);
}
First of all don't set the content type Spring WebServices will do that for you, messing around with that will only make things worse.
You should get the WebServiceConnection and cast that to a HeadersAwareSenderWebServiceConnection to add a header.
public class BasicAuthenticationInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String username="test#test";
String password="test";
byte[] userpassword = (username+":"+password).getBytes(UTF_8);
String encoded = Base64.getEncoder().encodeToString(userpassword);
WebServiceConnection conn = TransportContext.getConnection();
((HeadersAwareSenderWebServiceConnection) conn).addHeader("Authorization", "Basic " + encoded);
}
}
You also need to configure it. Assuming it is a bean don't call afterPropertiesSet (and ofcourse you are now using the ClientInterceptor remove the new Authentication() for your customized message sender.
The List<ClientInterceptor> will automatically create a list with all the interceptors so you can easily inject them.
#Bean
public WebServiceTemplate createWebServiceTemplate(Jaxb2Marshaller marshaller, List<ClientInterceptor> clientInterceptors) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
//SOAP URL
webServiceTemplate.setDefaultUri("http://host/Services.asmx");
webServiceTemplate.setCheckConnectionForFault(true);
webServiceTemplate.setInterceptors(clientInterceptors);
return webServiceTemplate;
}
If this doesn't work there is something else you are doing wrong and you will need to get in touch with the server developers and get more information on the error.
Update:
Apparently you also need to provide a SOAP Action in your request, which you currently don't. For this you can specify the SoapActionCallback in the marshalSendAndReceive method. Which action to specify you can find in the WSDL you are using.
SoapActionCallback soapAction = new SoapActionCallback("SoapActionToUse");
response = (AcquireTicketResponse) getWebServiceTemplate().marshalSendAndReceive(req, soapAction);

Secure Spring Boot API without user authentication

I'm currently developing a few services with spring boot and some security concerns came in mind. All the exposed endpoints are public and do not require any user/password authentication. However, these services cannot be easily accessible by a caller other than our front-end application, since we must gather some user information through a form, in which a captcha performs a validation. Because of that, we need to ensure that the services are only invoked by this front-end application and that fake requests are denied.
Due to these requirements, i initially thought that making the endpoints accessible via https was enough. Notwithstanding, the possibility of replay attacks and spoofing still concerns me.
So, reading a few articles i came up with the following draft:
Please refer the client as the front-end application.
client and server should share a key-pair (public and private keys).
For every request, the following must be satisfied:
client creates a unique nonce (random number)
client generates a HMAC-SHA1 token with the shared private key
token = hmac('sha1', private_key, public_key + timestamp + nonce);
client must send the public_key, timestamp, nonce and token in header
upon receiving a request, the server checks if all the header params are present and then calculates the same hmac-sha1 token and compares with the received value from the client.
the nonce is then added to a cache manager, so that duplicated requisitions are discarded.
if any of header parameters are missing or if the calculated token is different from the one sent by the client, the requesition is also discarded.
Is this an appropriate approach? Are the benefits of such overhead worth?
These are the codes i currently have:
#Service
public class APIAuthenticationManager implements AuthenticationManager {
#Value("${security.http.api_key}")
private String apiKeyValue;
#Value("${security.http.api_key_header}")
private String apiKeyRequestHeader;
#Value("${security.valid_timestamp.thresold}")
private String timestampThresold;
#Value("${security.valid_timestamp.header}")
private String timestampHeader;
#Value("${security.nonce.header}")
private String nonce;
#Value("${security.token.header}")
private String tokenHeader;
#Value("${security.private_key}")
private String privateKey;
#Override
public Authentication authenticate(Authentication authentication) {
HttpServletRequest request = (HttpServletRequest) authentication.getPrincipal();
if (!apiKeyValue.equals(request.getParameter(apiKeyRequestHeader))) {
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
String timestamp = request.getParameter(timestampHeader);
if(timestamp == null) {
throw new BadCredentialsException("Timestamp was not found or its value is invalid.");
}
Date requestIssueDate = Util.parseDate(timestamp, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
if(requestIssueDate == null) {
throw new BadCredentialsException("Timestamp was not found or its value is invalid.");
}
long expired = System.currentTimeMillis() - Integer.valueOf( timestampThresold );
if (requestIssueDate.getTime() > expired) {
throw new BadCredentialsException("Timestamp was not found or its value is invalid.");
}
// HMAC('SHA1', 'API_KEY', 'TOKEN GENERATED IN CLIENT');
// TOKEN GENERATED IN CLIENT = HMAC('SHA1', 'API_KEY', 'SECRET_KEY + TIMESTAMP + NONCE');
String tokenFromClient = request.getParameter(tokenHeader);
String calculatedToken = HMACSignatureUtil.calculateHMAC(privateKey, apiKeyValue + timestamp + nonce);
if(!tokenFromClient.equals(calculatedToken)) {
throw new BadCredentialsException("Invalid token.");
}
authentication.setAuthenticated(true);
return authentication;
}
This is the ConfigurerAdapter
#Autowired
private APIAuthenticationManager apiAuthenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
APIKeyAuthFilter filter = new APIKeyAuthFilter();
filter.setAuthenticationManager( apiAuthenticationManager );
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilter(filter).authorizeRequests().anyRequest().authenticated();
http.requiresChannel()
.anyRequest(). requiresSecure();
}

How to retrieve attributes and username sent by the CAS server with Spring Security

I have a spring boot application, which is MVC in nature. All page of this application are being authenticated by CAS SSO.
I have used "spring-security-cas" as described at https://www.baeldung.com/spring-security-cas-sso
Everything working fine as expected. However, I have one problem - that is, I cannot retrieve attributes
and username sent by the CAS server in the following #Bean. What need I do to retrieve all the attributes
and and username sent by the CAS server?
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties(serviceProperties());
provider.setTicketValidator(ticketValidator());
provider.setUserDetailsService(
s -> new User("casuser", "Mellon", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ADMIN")));
provider.setKey("CAS_PROVIDER_LOCALHOST_9000");
return provider;
}
First you will need to configure the attributeRepository source and the attributes to be retrieved, in attributeRepository section in CAS server, like:
cas.authn.attributeRepository.jdbc[0].singleRow=false
cas.authn.attributeRepository.jdbc[0].sql=SELECT * FROM USERATTRS WHERE {0}
cas.authn.attributeRepository.jdbc[0].username=username
cas.authn.attributeRepository.jdbc[0].role=role
cas.authn.attributeRepository.jdbc[0].email=email
cas.authn.attributeRepository.jdbc[0].url=jdbc:hsqldb:hsql://localhost:9001/xdb
cas.authn.attributeRepository.jdbc[0].columnMappings.attrname=attrvalue
cas.authn.attributeRepository.defaultAttributesToRelease=username,email,role
Check this example from CAS blog.
Then you need to implement an AuthenticationUserDetailsService at the service to read attributes returned from CAS authentication, something like:
#Component
public class CasUserDetailService implements AuthenticationUserDetailsService {
#Override
public UserDetails loadUserDetails(Authentication authentication) throws UsernameNotFoundException {
CasAssertionAuthenticationToken casAssertionAuthenticationToken = (CasAssertionAuthenticationToken) authentication;
AttributePrincipal principal = casAssertionAuthenticationToken.getAssertion().getPrincipal();
Map attributes = principal.getAttributes();
String uname = (String) attributes.get("username");
String email = (String) attributes.get("email");
String role = (String) attributes.get("role");
String username = authentication.getName();
Collection<SimpleGrantedAuthority> collection = new ArrayList<SimpleGrantedAuthority>();
collection.add(new SimpleGrantedAuthority(role));
return new User(username, "", collection);
}
}
Then, adjust your authenticationProvider with provider.setAuthenticationUserDetailsService(casUserDetailService);

Special character with Apache and Spring

I use the below code where I set credentials for basic https authentication to my server that uses Spring Security. Unfortunately I have problem with special characters like é,ò etc... I receive on server the question mark ? instead of correct character (both username and password). Someone know how to resolve it?
private RestClient(String username, String password) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(null, -1),
new UsernamePasswordCredentials(username, password));
HttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
public static synchronized RestClient getInstance(String username, String password){
if (instance == null){
instance = new RestClient(username, password);
instance.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
}
return instance;
}
and then I use for example
RestClient restClient = RestClient.getInstance(username, password);
if (queryParams!=null && queryParams.length!=0){
response = restClient.getForObject(addQueryParam(url, queryParams), Response.class);
}
I've tried even with URIEncoding="UTF-8" into server.xml of tomcat but the issue is still present.
UPDATE maybe I have fixed the issue setting the header manually:
private RestClient(String username, String password) {
String credential = Base64.getEncoder().encodeToString((username+":"+password).getBytes());
// create custom http headers for httpclient
List<BasicHeader> defaultHeaders = Arrays.asList(new BasicHeader(HttpHeaders.AUTHORIZATION, "Basic "+credential));
HttpClient httpClient = HttpClients.custom().setDefaultHeaders(defaultHeaders).build();
setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
public static synchronized RestClient getInstance(String username, String password){
if (instance == null){
instance = new RestClient(username, password);
}
return instance;
}
I'm testing, let you know

Spring Rest template with http client for NTLM authentication

We have a web service deployed in IIS server which authenticate based on NTLM authentication.
When i try to access the web service by passing username and password in httpCleint UserNamePasswordCredentials, am getting warnings as
NTLM authentication error: Credentials cannot be used for NTLM authentication: org.apache.http.auth.UsernamePasswordCredentials
Please clarify how to user http client with spring rest template to pass the NTLM authentication with user name and password.
Note:Though am getting the warning message, am also getting response.
Just define the following class.
public class NtlmAuthenticator extends Authenticator {
private final String username;
private final char[] password;
public NtlmAuthenticator(final String username, final String password) {
super();
this.username = username;
this.password = password.toCharArray();
}
#Override
public PasswordAuthentication getPasswordAuthentication() {
return (new PasswordAuthentication(username, password));
}
}
then add the following code.Thats it.It started working.
NtlmAuthenticator authenticator = new NtlmAuthenticator(userName,
password);
Authenticator.setDefault(authenticator);

Resources