Map LDAP groups to OAuth2 UserDetails in Spring Boot web app - spring

After failed attempts to get CAS Single Sign Out working, we decided to try out OAuth2 using OpenID Connect with our Spring Boot web apps. Now we only need OAuth for SSO and to provide authentication - we're not storing username/password in a db or anything. We are using Google LDAP to grant authorities. I cannot find any solid example of using Google LDAP for authorities with OAuth2. My OAuth2 code is pretty simple so far. Here's my WebSecurityConfig so far:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.mvcMatchers("/home").permitAll()
.anyRequest().authenticated())
.oauth2Login(oauthLogin -> oauthLogin.permitAll());
}
and one of my endpoints that works:
#GetMapping("/user/me")
public String userDetails(#AuthenticationPrincipal OAuth2User user) {
Map<String, Object> attributes = user.getAttributes();
String username = (String) attributes.get("email");
return username;
}
Now here is what I used with my CAS and LDAP config. This block of code would pull all LDAP groups and map them to roles using SimpleGrantedAuthority. I've tried a ton of different configs, but nothing has worked.
private LdapContext getLdapReadContext(){
LdapContext ctx = null;
try{
Hashtable<String,String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY,ldapCtxFactory);
env.put(Context.SECURITY_AUTHENTICATION,"Simple");
env.put(Context.SECURITY_PRINCIPAL,ldapUsername);
env.put(Context.SECURITY_CREDENTIALS,ldapPassword);
env.put(Context.PROVIDER_URL,ldapURL);
ctx = new InitialLdapContext(env,null);
}catch(NamingException e){
e.printStackTrace();
}
return ctx;
}
public List<String> Groups(LdapContext ctx, AttributePrincipal principal){
String uid = "uid";
List<String> groups = new LinkedList<>();
NamingEnumeration answer = null;
if(principal!=null){
Map attributes =principal.getAttributes();
if(attributes.get(uid)!=null){
uid=attributes.get(uid).toString();
try{
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectClass=groupOfNames)(member=uid="+uid+","+ldapBase+"))";
String[] attrIDs = {"cn"};
constraints.setReturningAttributes(attrIDs);
answer = ctx.search(ldapGroupBase,searchFilter,constraints);
while (answer.hasMore()){
Attributes attrs = ((SearchResult)answer.next()).getAttributes();
groups.add(attrs.get("cn").toString());
}
}catch(Exception ignored){
}finally{
if (answer !=null){
try{
answer.close();
}catch(Exception ignored){
}
}
if (ctx != null){
try{
ctx.close();
}catch(Exception ignored){
}
}
}
}
}
if (groups.isEmpty()){
groups.add("STUDENT");
}
return groups;
}
#Override
public UserDetails loadUserDetails (#Autowired Authentication authentication) throws UsernameNotFoundException {
CasAssertionAuthenticationToken casAssertionAuthenticationToken = (CasAssertionAuthenticationToken) authentication;
AttributePrincipal principal = casAssertionAuthenticationToken.getAssertion().getPrincipal();
Map attributes = principal.getAttributes();
String user = (String)attributes.get("username");
String email = (String)attributes.get("email");
String fname = (String)attributes.get("fname");
String lname = (String)attributes.get("lname");
String VNumber = (String)attributes.get("UDC_IDENTIFIER");
String uid = (String)attributes.get("uid");
String role = "user";
String username = authentication.getName();
List<String> grouplist = null;
grouplist = Groups(getLdapReadContext(), principal);
Collection<SimpleGrantedAuthority> collection = new ArrayList<SimpleGrantedAuthority>();
for (int i =0; i<grouplist.size(); i++) {
collection.add(new SimpleGrantedAuthority((grouplist.get(i)).substring(4)));
}
return new User(username, "",collection);
}

Related

Unable to crate a Firestore Bean, BeanCreationException. java.lang.NoClassDefFoundError: com/google/api/gax/rpc/TransportChannelProvider

I am working with Spring Boot and Spring Security, but is my first time using Google cloud firebase Firestore, I am connecting to my database via Firebase Admin SDK. I have a configuration class that looks like this.
#Configuration
public class FirestoreUserConfig {
#Bean
public Firestore getDB() {
return FirestoreClient.getFirestore();
}
}
I have BeanCreationException, my stack trace, in summary, looks like this.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getDB' defined in class path resource [com/d1gaming/user/firebaseconfig/FirestoreUserConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.google.cloud.firestore.Firestore]: Factory method 'getDB' threw exception; nested exception is java.lang.NoClassDefFoundError: com/google/api/gax/rpc/TransportChannelProvider
Caused by: java.lang.ClassNotFoundException: com.google.api.gax.rpc.TransportChannelProvider
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_271]
at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_271]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[na:1.8.0_271]
at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_271]
... 105 common frames omitted
The weird thing is I followed Google's documentation on Initializing firebase Admin SDK, I added the corresponding dependencies to my pom.xml file but it does not seem to work anyway. This is my Admin SDK dependency.
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>7.0.1</version>
</dependency>
I imagine Maven is not able to find this TransportChannelProvider class, Im not sure. I also tried adding all of gcp core dependencies in case provided Admin SDK dependency didn't have the class but I had the same result. I am also making use of a Service and a RESTController for my back end application, I don't know if it is relevant to include it here but I am going to include it just in case.
#Service
public class UserService {
public final String USERS_COLLECTION = "users";
#Autowired
private PasswordEncoder passwordEncoder;
private Firestore firestore;
//get users collcetion from Firestore.
private CollectionReference getUsersCollection() {
return firestore.collection(this.USERS_COLLECTION);
}
//Post a user onto the user collection. documentID is auto-generated by firestore.
public String saveUser(User user) throws InterruptedException, ExecutionException {
Query query = getUsersCollection().whereEqualTo("userName", user.getUserName());
QuerySnapshot querySnapshot = query.get().get();
//Query to validate if userName is already in use.
if(querySnapshot.isEmpty()) {
ApiFuture<DocumentReference> document = getUsersCollection().add(user);
DocumentReference reference = document.get();
String userId = document.get().getId();
//Assign auto-generated Id to userId field for ease of querying.
WriteBatch batch = FirestoreClient.getFirestore().batch();
batch.update(reference, "userId",userId);
//ENCODE USER PASSWORD!
batch.update(reference, "userPassword",passwordEncoder.encode(reference.get().get().toObject(User.class).getUserPassword()));
List<WriteResult> results = batch.commit().get();
results.forEach(result -> {
System.out.println("Update Time: " + result.getUpdateTime());
});
return "Created user with ID: " + "'" + userId + "'";
}
return "Username is already in use";
}
//Get User by given its email.
public Optional<User> getUserByEmail(String userEmail) throws InterruptedException, ExecutionException{
Query query = getUsersCollection().whereEqualTo("userEmail", userEmail);
QuerySnapshot snapshot = query.get().get();
//if user with provided Email exists in collection.
if(!snapshot.isEmpty()) {
List<User> userLs = snapshot.toObjects(User.class);
//Since there is a unique email for each document,
//There will only be on User object on list, we will retrieve the first one.
for(User currUser: userLs) {
return Optional.of(currUser);
}
}
return null;
}
//Query for user by given userName.
public Optional<User> getUserByUserName(String userName) throws InterruptedException, ExecutionException{
//Perform a query based on a user's Name.
Query query = getUsersCollection().whereEqualTo("userName", userName);
QuerySnapshot snapshot = query.get().get();
if(!snapshot.isEmpty()) {
List<User> userList = snapshot.toObjects(User.class);
//Since there is a unique userName for each document,
//there will only be one User object on the list, we will retrieve the first one.
for(User currUser: userList) {
return Optional.of(currUser);
}
}
return null;
}
//Query for user by given userName.
public User getUserByName(String userName) throws InterruptedException, ExecutionException {
//Perform a query based on a user's Name.
Query query = getUsersCollection().whereEqualTo("userName", userName);
QuerySnapshot snapshot = query.get().get();
if(!snapshot.isEmpty()) {
List<User> userList = snapshot.toObjects(User.class);
//Since there is a unique userName for each document,
//there will only be one User object on the list, we will retrieve the first one.
for(User currUser : userList) {
return currUser;
}
}
return null;
}
//Get User by its auto-generated ID.
public User getUserById(String userId) throws InterruptedException, ExecutionException {
DocumentReference reference = getUsersCollection().document(userId);
if(reference.get().get().exists()) {
DocumentSnapshot snapshot = reference.get().get();
User user = snapshot.toObject(User.class);
return user;
}
return null;
}
//Get DocumentReference on a User.
public DocumentReference getUserReference(String userId) throws InterruptedException, ExecutionException {
DocumentReference reference = getUsersCollection().document(userId);
//Evaluate if documentExists in users collection.
if(reference.get().get().exists()) {
return reference;
}
return null;
}
//return a list of objects located in the users collection.
public List<User> getAllUsers() throws InterruptedException, ExecutionException {
//asynchronously retrieve all documents
ApiFuture<QuerySnapshot> future = getUsersCollection().get();
List<QueryDocumentSnapshot> objects = future.get().getDocuments();
//If there is no documents, return null.
if(!objects.isEmpty()) {
List<User> ls = new ArrayList<>();
objects.forEach((obj) -> {
User currUser = obj.toObject(User.class);
ls.add(currUser);
});
return ls;
}
return null;
}
//delete a User from users collection by a given id.
//In reality delete method just changes UserStatus from active to inactive or banned.
public String deleteUserById(String userId) throws InterruptedException, ExecutionException {
Firestore db = FirestoreClient.getFirestore();
DocumentReference reference = db.collection(USERS_COLLECTION).document(userId);
User user = reference.get().get().toObject(User.class);
if(user == null) {
return "User not found.";
}
WriteBatch batch = db.batch();
batch.update(reference, "userStatusCode",UserStatus.INACTIVE);
ApiFuture<List<WriteResult>> result = batch.commit();
List<WriteResult> results = result.get();
results.forEach(response -> {
System.out.println("Update Time:" + response.getUpdateTime());
});
//Check if user did actually change status.
if(reference.get().get().toObject(User.class).getStatusCode().equals(UserStatus.ACTIVE)) {
return "User with ID: " + "'" + userId + "'" + " was deleted.";
}
return "User could not be deleted";
}
//Delete a User's certain field value.
public String deleteUserField(String userId, String userField) throws InterruptedException, ExecutionException {
Firestore firestore = FirestoreClient.getFirestore();
DocumentReference reference = getUsersCollection().document(userId);
if(!reference.get().get().exists()) {
return "User not found.";
}
Map<String,Object> map = new HashMap<>();
map.put(userField, FieldValue.delete());
WriteBatch batch = firestore.batch();
batch.update(reference, map);
List<WriteResult> results = batch.commit().get();
results.forEach(response -> System.out.println("Update Time: " + response.getUpdateTime()));
return "Field deleted Successfully";
}
//Change UserStatus to BANNED
public String banUserById(String userId) throws InterruptedException, ExecutionException {
Firestore db = FirestoreClient.getFirestore();
final DocumentReference reference = db.collection(this.USERS_COLLECTION).document(userId);
WriteBatch batch = db.batch().update(reference,"userStatusCode",UserStatus.BANNED);
List<WriteResult> results = batch.commit().get();
results.forEach(response -> System.out.println("Update Time: " + response.getUpdateTime()));
if(reference.get().get().toObject(User.class).getStatusCode().equals(UserStatus.BANNED)) {
return "User with ID: " + "'" + userId + "'" + " was BANNED.";
}
return "User could not be BANNED.";
}
//Set a user with all new fields.
public String updateUser(User user) throws InterruptedException, ExecutionException {
Firestore firestore = FirestoreClient.getFirestore();
final DocumentReference reference = getUsersCollection().document(user.getUserId());
DocumentSnapshot snapshot = reference.get().get();
if(snapshot.exists()) {
WriteBatch batch = firestore.batch();
batch.set(reference, user);
List<WriteResult> results = batch.commit().get();
results.forEach(response -> System.out.println("Update time: " + response.getUpdateTime()));
return "User updated successfully";
}
return "User not found.";
}
// Update a specific field on a given document by another given value. In case userId is field to be changed, one integer will be subtracted from userTokens field.
public String updateUserField(String userId,String objectField, String replaceValue) throws InterruptedException, ExecutionException {
Firestore db = FirestoreClient.getFirestore();
final DocumentReference reference = getUsersCollection().document(userId);
if(!reference.get().get().exists()) {
return "User not found.";
}
WriteBatch batch = db.batch();
List<WriteResult> results = new ArrayList<>();
//These fields cannot be updated.
if(!objectField.equals("userName") && !objectField.equals("userCash") && !objectField.equals("userTokens") && !objectField.equals("userId")) {
batch.update(reference, objectField, replaceValue);
results = batch.commit().get();
results.forEach(response ->{
System.out.println("Update time: " + response.getUpdateTime());
});
}
else if(objectField.equals("userName")) {
String response = updateUserName(userId, replaceValue);
return response;
}
else {
return "This field canntot be updated.";
}
return "User field could not be updated.";
}
//Update a user's userName depending of availability and token adquisition capacity. i.e. if user has enough tokens to pay fee.
public String updateUserName(String userId, String newUserName) throws InterruptedException, ExecutionException {
Firestore db = FirestoreClient.getFirestore();
final DocumentReference reference = getUsersCollection().document(userId);
DocumentSnapshot snapshot = reference.get().get();
if(!snapshot.exists()) {
return "User not found.";
}
Query query = getUsersCollection().whereEqualTo("userName", newUserName);
QuerySnapshot querySnapshot = query.get().get();
//Evaluate if userName is already in use.
String response = "Username is already taken";
if(querySnapshot.isEmpty()) {
//Transaction to get() tokens and update() tokens.
ApiFuture<String> futureTransact = db.runTransaction(transaction -> {
DocumentSnapshot doc = transaction.get(reference).get();
double tokens = doc.getDouble("userTokens");
//evaluate if user holds more than one token
if(tokens >= 1) {
transaction.update(reference, "userTokens", tokens - 1);
transaction.update(reference, "userName", newUserName);
return "Username updated to: '"+ newUserName +"'";
}
else {
throw new Exception("Not enough Tokens");
}
});
response = futureTransact.get();
}
return response;
}
//update user Currency field.
public String updateUserCash(String userId, double cashQuantity) throws InterruptedException, ExecutionException {
Firestore firestore = FirestoreClient.getFirestore();
final DocumentReference reference = getUsersCollection().document(userId);
DocumentSnapshot snapshot = reference.get().get();
String response = "User not found.";
//evaluate if document exists
if(snapshot.exists()) {
ApiFuture<String> futureTransaction = firestore.runTransaction(transaction -> {
Map<String,Object> map = new HashMap<>();
map.put("userCash", FieldValue.increment(cashQuantity));
transaction.update(reference, map);
return "Updated userCash";
});
response = futureTransaction.get();
return response;
}
return response;
}
//Update user Token field.
public String updateUserTokens(String userId, double tokenQuantity) throws InterruptedException, ExecutionException {
Firestore firestore = FirestoreClient.getFirestore();
final DocumentReference reference = getUsersCollection().document(userId);
String response = "User not found.";
//evaluate if user exists on collection.
if(reference.get().get().exists()) {
ApiFuture<String> futureTransaction = firestore.runTransaction(transaction -> {
Map<String,Object> map = new HashMap<>();
map.put("userTokens",tokenQuantity);
transaction.update(reference, map);
return "Updated userTokens";
});
response = futureTransaction.get();
return response;
}
return response;
}
//evaluate if given documentId exists on given collection.
public static boolean isPresent(String userId,String collectionName) throws InterruptedException, ExecutionException {
Firestore db = FirestoreClient.getFirestore();
DocumentReference reference = db.collection(collectionName).document(userId);
ApiFuture<DocumentSnapshot> snapshot = reference.get();
DocumentSnapshot document = snapshot.get();
if(!document.exists()) {
return false;
}
return true;
}
//Evaluate if given document's status corresponds to active.
public static boolean isActive(String userId, String collectionName) throws InterruptedException, ExecutionException {
Firestore db = FirestoreClient.getFirestore();
DocumentReference reference = db.collection(collectionName).document(userId);
ApiFuture<DocumentSnapshot> snapshot = reference.get();
DocumentSnapshot result = snapshot.get();
User user = result.toObject(User.class);
if(user.getStatusCode().equals(UserStatus.ACTIVE)) {
return true;
}
return false;
}
}
My Controller class:
#RestController
#RequestMapping("/userapi")
#CrossOrigin(origins = "http://localhost:4200")
public class UserController {
#Autowired
UserService userServ;
#GetMapping(value = "/users/search",params="userName")
#PreAuthorize("hasRole('PLAYER')")
public ResponseEntity<Object> getUserByName(#RequestParam(value = "userName", required = true)final String userName) throws InterruptedException, ExecutionException{
if(userName == null) {
return new ResponseEntity<>("Invalid Input",HttpStatus.BAD_REQUEST);
}
User user = userServ.getUserByName(userName);
if(user == null) {
return new ResponseEntity<>("User Not Found", HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(user,HttpStatus.OK);
}
#GetMapping("/users")
#PreAuthorize("hasRole('PLAYER')")
public ResponseEntity<List<User>> getAllUsers() throws InterruptedException, ExecutionException{
List<User> ls = userServ.getAllUsers();
if(ls.isEmpty()) {
return new ResponseEntity<List<User>>(ls, HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<User>>(ls, HttpStatus.OK);
}
#DeleteMapping(value = "/users/delete",params="userId")
#PreAuthorize("hasRole('ADMINISTRATOR')")
public ResponseEntity<Object> deleteUserById(#RequestParam(value="userId", required = true)String userId, #RequestParam(required = false, value="userField") String userField) throws InterruptedException, ExecutionException{
if(userField != null) {
String response = userServ.deleteUserField(userId, userField);
if(response.equals("User not found.")) {
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
String response = userServ.deleteUserById(userId);
if(response.equals("User not found.")) {
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
#PutMapping(value = "/users/update")
#PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('PLAYER')")
public ResponseEntity<Object> updateUser(#RequestBody User user) throws InterruptedException, ExecutionException{
String response = userServ.updateUser(user);
if(response.equals("User not found.")) {
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
#PutMapping(value = "/users/update",params="userId")
#PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('PLAYER')")
public ResponseEntity<Object> updateUserField(#RequestParam(required = true, value="userId")String userId, #RequestParam(required = true)String userField, #RequestParam(required = true)String replaceValue) throws InterruptedException, ExecutionException{
String response = userServ.updateUserField(userId, userField, replaceValue);
if(response.equals("User not found.")) {
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
else if(response.equals("This field cannot be updated.")) {
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
I didn't find ANY documentation on this particular problem and that is why I am here, I would be glad if anyone could help me solving this problem. Am I missing something? Anyways thank you for your time, happy coding.
as you said, you are missing the TransportChannelProvider interface and it's implementations. it's not provided in the dependency you added.
try to add this dependency to your POM:
<dependency>
<groupId>com.google.api</groupId>
<artifactId>gax</artifactId>
<version>1.57.0</version>
</dependency>
the interface included there.
After checking my pom.xml and my dependency hierarchies(On Eclipse, found on the toolbar above the console when the pom.xml file is being viewed.), I found I had another dependency on another Spring Boot project that was overriding the one I had on my project, make sure to check all dependencies on different projects as well. The dependency version I had on my other project did not include the classes Spring needed to create a a bean.

Embedding Apache FTP Server : Not able to login with 40 user Concurrently

I am using this Embedded server for integration testing of Spring-integration-ftp.
I have to connect this ftp server to 40 spring integration ftp service I have also added ConcurrentLoginPermission to 50 still I am getting
SENT: 421 Maximum login limit has been reached. Error
Here is the code Which I have written for the embedded server.
protected static final Integer FTP_PORT = 2221;
protected static final String FTP_USER = "admin";
protected static final String FTP_PASSWORD = "admin";
protected static final String FTP_HOME_DIRECTORY = "src/test/resources/xml/checkpoint";```
```PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
UserManager userManager = userManagerFactory.createUserManager();
BaseUser user = new BaseUser();
user.setName(FTP_USER);
user.setPassword(FTP_PASSWORD);
user.setHomeDirectory(FTP_HOME_DIRECTORY);
List<Authority> authorities = new ArrayList<>();
authorities.add(new WritePermission());
authorities.add(new TransferRatePermission(50, 50));
authorities.add(new ConcurrentLoginPermission(50, 50));
user.setAuthorities(authorities);
try {
userManager.save(user);
} catch (FtpException e) {
e.printStackTrace();
}
ListenerFactory listenerFactory = new ListenerFactory();
listenerFactory.setPort(FTP_PORT);
FtpServerFactory factory = new FtpServerFactory();
factory.setUserManager(userManager);
factory.addListener("default", listenerFactory.createListener());
FtpServer server = factory.createServer();
try {
server.start();
} catch (FtpException e) {
e.printStackTrace();
}
FakeFtpServer fakeFtpServer = new FakeFtpServer();
fakeFtpServer.setServerControlPort(2222);
FileSystem fileSystem = new UnixFakeFileSystem();
fileSystem.add(new DirectoryEntry("/FTP_TEST"));
fakeFtpServer.setFileSystem(fileSystem);
UserAccount userAccount = new UserAccount("vioohcentral", "vioohcentral", "/FTP_TEST");
fakeFtpServer.addUserAccount(userAccount);
fakeFtpServer.start();

Keycloak.json configuration file import

I have configured my goclient with keycloak.I am reading the keycloak server details from keycloak.json file which is present inside my Go client.
My whole setup is in Kubernetes cluster.Everytime when new keycloak server comes up how should I pass keycloak.json file to the Go client so that it can read the details from json file?
Is there a way I can get the keycloak.json file with all the client configuration details without having to login to keycloak and download it ?
"realm": "SolutionAdmin",
"auth-server-url": "localhost:8081/auth",
"ssl-required": "none",
"resource": "admin",
"credentials": {
"secret": "6ee0f523-c392-4406-bb14-ba315125c6ea"
},
"confidential-port": 0
}
if you want to have a pre-configured embedded keycloak server, you'll need to extend KeycloakApplication and have it configured to trigger in lieu of the base class. in this example, KeycloakProperties is just a representation of all the keycloak properties we used in application.properties. but you can get the gist of it. fair warning: i did not write this, but am figuring out how a coworker did for another project.
public class EmbeddedKeycloakApplication extends KeycloakApplication {
public final KeycloakProperties keycloakProperties;
public EmbeddedKeycloakApplication() {
super();
keycloakProperties = SpringContextAdapter.getBean(KeycloakProperties.class);
System.getProperties().putAll(keycloakProperties.getRealmProperties());
createMasterRealmAdminUser();
createRealm();
}
private void createMasterRealmAdminUser() {
log.debug("Creating administrative user.");
String username = keycloakProperties.getServer(KeycloakProperties.ADMIN_USER_USERNAME);
String password = keycloakProperties.getServer(KeycloakProperties.ADMIN_USER_PASSWORD);
String email = keycloakProperties.getServer(KeycloakProperties.ADMIN_USER_EMAIL);
KeycloakSession session = getSessionFactory().create();
ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
try {
session.getTransactionManager().begin();
if (!applianceBootstrap.isNoMasterUser()) {
log.debug("Administrative user already exists. No work to do.");
return;
}
applianceBootstrap.createMasterRealmUser(username, password);
RealmModel adminRealm = session.realms().getRealm(Config.getAdminRealm());
UserModel adminUser = session.users().getUserByUsername(username, adminRealm);
adminUser.setEmail(email);
adminUser.setEmailVerified(true);
session.getTransactionManager().commit();
log.info("Created administrative user {}", username);
} catch (Exception ex) {
log.error("Couldn't create keycloak master admin user: {}", ex.getMessage());
session.getTransactionManager().rollback();
}
session.close();
}
private void createRealm() {
String realmImportFilename = keycloakProperties.getRealmImportFile();
KeycloakSession session = getSessionFactory().create();
String realmId = keycloakProperties.getRealm(KeycloakProperties.REALM_ID);
try {
session.getTransactionManager().begin();
RealmManager manager = new RealmManager(session);
if (manager.getRealm(realmId) != null) {
log.debug("{} realm already exists. No work to do.", realmId);
return;
}
Resource realmImportFile = new ClassPathResource(realmImportFilename);
RealmRepresentation rep =
JsonSerialization.readValue(
realmImportFile.getInputStream(), RealmRepresentation.class, true);
manager.importRealm(rep);
log.info("Imported Realm json file {}", realmImportFilename);
session.getTransactionManager().commit();
} catch (Exception e) {
log.error("Failed to import Realm json file {}: {}", realmImportFilename, e.getMessage(), e);
session.getTransactionManager().rollback();
}
session.close();
}
}
#Configuration
public class EmbeddedKeycloakConfig {
#Bean
ServletRegistrationBean<HttpServlet30Dispatcher> keycloakJaxRsApplication(
KeycloakProperties keycloakProperties, DataSource dataSource) throws NamingException {
mockJndiEnvironment(dataSource);
var contextPath = keycloakProperties.getServer(KeycloakProperties.SERVER_CONTEXT_PATH);
ServletRegistrationBean<HttpServlet30Dispatcher> servlet =
new ServletRegistrationBean<>(new HttpServlet30Dispatcher());
servlet.addInitParameter(
"javax.ws.rs.Application", EmbeddedKeycloakApplication.class.getName());
servlet.addInitParameter(
ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, contextPath);
servlet.addInitParameter(ResteasyContextParameters.RESTEASY_USE_CONTAINER_FORM_PARAMS, "true");
servlet.addInitParameter(ResteasyContextParameters.RESTEASY_DISABLE_HTML_SANITIZER, "true");
servlet.addUrlMappings(contextPath + "/*");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);
return servlet;
}
#Bean
FilterRegistrationBean<EmbeddedKeycloakRequestFilter> keycloakSessionManagement(
KeycloakProperties keycloakProperties) {
FilterRegistrationBean<EmbeddedKeycloakRequestFilter> filter = new FilterRegistrationBean<>();
filter.setName("Keycloak Session Management");
filter.setFilter(new EmbeddedKeycloakRequestFilter());
filter.addUrlPatterns(
keycloakProperties.getServer(KeycloakProperties.SERVER_CONTEXT_PATH) + "/*");
return filter;
}
private void mockJndiEnvironment(DataSource dataSource) throws NamingException {
NamingManager.setInitialContextFactoryBuilder(
env ->
environment ->
new InitialContext() {
#Override
public Object lookup(Name name) {
return lookup(name.toString());
}
#Override
public Object lookup(String name) {
if ("spring/datasource".equals(name)) {
return dataSource;
}
return null;
}
#Override
public NameParser getNameParser(String name) {
return CompositeName::new;
}
#Override
public void close() {
// NOOP
}
});
}
}

Consuming Soap Service in spring boot application

I need to consume a soap service in spring boot. How can i do that easily using annotations like we do for Rest. I need to send headers, form the body for my service. Please help me with the solution
public String sendMessage(String processInstanceId) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
String request = "<SOAP:Envelope xmlns:" + "SOAP='http://schemas.xmlsoap.org/soap/envelope/'>" + "<SOAP:Body>"
+ "<SendMessage xmlns='http://schemas.cordys.com/bpm/execution/1.0'>" + "<receiver>" + processInstanceId
+ "</receiver>" + "<message overwrite='false' />" + "</SendMessage>" + "</SOAP:Body>"
+ "</SOAP:Envelope>";
SendMessageAPI sendMessageObject = new SendMessageAPI();
StreamSource source = new StreamSource(new StringReader(request));
StreamResult result = new StreamResult(System.out);
System.out.println("called service" + request);
webServiceTemplate.sendSourceAndReceiveToResult(
"url",
source, result);
return "Success";
You may use Spring Web Service where it's present the WebServiceTemplate similar to the RestTemplate
In order to add SOAP Header and/or HTTP Header you can implement the WebServiceMessageCallback interface.
Here a simple example for adding HTTP Headers
The WebServiceMessageCallback implementation (note I'm using Axiom as MessageFactory)
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;
}
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 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);
}
}
}
The WebServiceMessageCallback usage:
WebServiceResponse resp = (WebServiceResponse)webSvcTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, "http://ws.com/soapAction") );
I hope it's usefull
Angelo

spring authentication for rest api to include hardcoded and lookup

I have a rest API application that authenticates requests by verifying credentials with a database lookup. I need to now add an additional feature that will allow a specific set of credentials to be allowed that is not a record in the database table. Basically I need to hardcode these credentials; but what I found is that the spring authenticationprovider for userDetails does not authenticate this and I am not sure why or how. I added an if statement just before retrieving the dataset result to validate the user credentials but it still does not work.Here is my code:
#SuppressWarnings("deprecation")
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException
{
Logger logger = LoggerFactory.getLogger("com.reader.AuthService.loadUserByUsername");
logger.info("Request--");
UserDetails userDt = null;
logger.info("USER:" + username);
Connection con = null;
String query = "SELECT * FROM Customers WHERE username= ?";
logger.info("Making SQL request");
try
{
con= dataSource.getConnection();
String password = null;
String authority = null;
int enabled = 0;
try(Connection dbConn = dataSource.getConnection();
PreparedStatement pst = dbConn.prepareStatement(query);)
{
pst.setString(1, username);
ResultSet rs = pst.executeQuery();
if(username.equalsIgnoreCase("sampleUser")){
username = "sampleUser";
password="1234567_sample";
List ls = new ArrayList();
ls.add(new GrantedAuthorityImpl("ROLE_USER"));
userDt = new User(username, password, enabled==1?true:false , true, true, true, ls);
return userDt;
}
if (rs.next())
{
username = rs.getString("username");
password = rs.getString("password");
authority = "ROLE_USER";
enabled = rs.getInt("isactive");
List ls = new ArrayList();
ls.add(new GrantedAuthorityImpl(authority));
userDt = new User(username, password, enabled==1?true:false , true, true, true, ls);
}
else
{
System.out.println("No credentials present in DB for username" + username);
throw new UsernameNotFoundException(
"Bad Credentials", username);
}
}
catch(SQLException e)
{
System.out.println("API: "+e.getMessage());
e.printStackTrace();
}
con.close();
}
catch(UsernameNotFoundException e)
{
throw e;
}
catch(Exception e1)
{
e1.printStackTrace();
}
return userDt;
In the server-context file I declare the user ref service to this class file above:
<beans:bean id="authservice"
class="com.reader.security.AuthService" />
<security:authentication-manager>
<security:authentication-provider
user-service-ref="authservice" />
</security:authentication-manager>
I think the problem starts here in AbstractUserDetailsAuthenticationProvider:
try {
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} else {
throw exception;
}
}
here is the exception:
org.springframework.security.authentication.DisabledException: User is disabled
could someone please assist or at least explain how this authentication works.
EDIT:
console response:
10:29:04.703 [http-nio-8080-exec-2]
DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Basic
Authentication Authorization header found for user
'internalUser'
10:29:04.705 [http-nio-8080-exec-2]
DEBUG o.s.s.authentication.ProviderManager - Authentication attempt
using org.springframework.security.authentication.dao.
DaoAuthenticationProvider
10:29:04.707 [http-nio-8080-exec-2] INFO
c.b.f.AuthService.loadUserByUsername - Request--
10:29:04.707 [http-nio-8080-exec-2] INFO
c.b.f.AuthService.loadUserByUsername - USER:internalUser
10:29:04.707 [http-nio-8080-exec-2] INFO
c.b.f.AuthService.loadUserByUsername - Making SQL request
AbandonedObjectPool is
used (org.apache.commons.dbcp.AbandonedObjectPool#3517a554)
LogAbandoned: false
RemoveAbandoned: true
RemoveAbandonedTimeout: 300
10:32:51.434 [http-nio-8080-exec-2]
DEBUG o.s.s.a.d.DaoAuthenticationProvider - User account is disabled
10:37:12.083 [http-nio-8080-exec-2]
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached
instance of singleton
bean 'userAuthenticationErrorHandler'
10:37:12.083 [http-nio-8080-exec-2]
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance
of singleton bean 'org.springframework.context.annotation.
internalScheduledAnnotationProcessor'
It's because your code is setting enabled as false as you initialized enabled integer to zero and later below enabled==1?true:false which will always return false. So i think you missed to set enabled = 1 in case of your "sampleUser" inside if statement
int enabled = 0;
if(username.equalsIgnoreCase("sampleUser")){
username = "sampleUser";
password="1234567_sample";
List ls = new ArrayList();
ls.add(new GrantedAuthorityImpl("ROLE_USER"));
userDt = new User(username, password, enabled==1?true:false , true, true, true, ls);
return userDt;
}
loadUserByUsername(String username) gets called when authentication is completed
for authentication purpose you need to override authenticate() and use it as given below
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider{
#Autowired
private CustomUserService userService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
CustomUser user = userService.loadUserByUsername(username);
if (user == null || !user.getUsername().equalsIgnoreCase(username)) {
throw new BadCredentialsException("Username not found.");
}
if (!password.equals(user.getPassword())) {
throw new BadCredentialsException("Wrong password.");
}
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
public boolean supports(Class<?> arg0) {
return true;
}
}
and declare it in security xml
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>

Resources