Spring Ldaptemplate 2 add member to group - spring

what is best way to add member in group using spring LdapTemplate.
I have successfully create user and remove user ex.
But I'm trying to add member then I'm facing issue.
Add Member Code:
public boolean addMemberToGroup(List<String> groupList, User user) {
boolean isAddedSuccessfully=false;
try{
for(int i=0;i<groupList.size();i++){
Name groupDn = buildGroupDn(groupList.get(i));
DirContextOperations ctx = ldapTemplate.lookupContext(groupDn);
ctx.addAttributeValue("member",buildPersonDn(user.getUid()));
ldapTemplate.update(ctx);
}
isAddedSuccessfully=true;
}
catch(Exception e){
isAddedSuccessfully=false;
}
return isAddedSuccessfully;
}
private Name buildGroupDn(String groupName) {
return LdapNameBuilder.newInstance("cn=groups").add("cn", groupName).build();
}
private Name buildPersonDn(String userID) {
return LdapNameBuilder.newInstance()
.add("uid", userID).add("cn", "users")
.build();
}
Exception Of addMemberToGroup: Class class org.springframework.ldap.core.DirContextAdapter must have a class level interface org.springframework.ldap.odm.annotations.Entry annotation.
please let me know what i am missing.

The updatemethod is intended for use with ODM annotated classes. When working with DirContextAdapter you should use the modifyAttributes method.

Though this is an old question, it's one I had too recently. For anyone coming here in the future, this question is similar to the one found at Spring Malformed 'member' attribute value. Putting together info from both questions, I found the solution to my problem, and share here what worked for me.
In addition to using the modifyAttributes method of the LdapTemplate class, it's also important to pass a String object as the second parameter of the addAttributeValue method of the DirContextOperations class when adding a member, rather than passing a Name object. The toString method called on the corresponding Name object does the trick.
Original code:
DirContextOperations ctx = ldapTemplate.lookupContext(groupDn);
ctx.addAttributeValue("member",buildPersonDn(user.getUid()));
ldapTemplate.update(ctx);
Try this instead:
DirContextOperations ctx = ldapTemplate.lookupContext(groupDn);
ctx.addAttributeValue("member",buildPersonDn(user.getUid()).toString());
ldapTemplate.modifyAttributes(ctx);

Related

How can I throw mapping to object exception with gson?

I'm trying to force gson to throw an exception when an string does not map to an object which I'm passing to it.
#ResponseStatus(HttpStatus.CREATED)
#PostMapping("offer")
public String postOffer(#RequestBody String jsonBody) {
Offer offer = gson.fromJson(jsonBody, Offer.class);
offerRepository.save(offer);
return offer.getId();
}
Currently, it will just save what ever it can to the db and ignore any elements that don't map to the class. This is bad for me because I get bad data making it's way to the db.
Any help would be appreciated.
ps. using springboot-data-mongodb and gson for mapping.
Thanks
In GSON you cannot make some fields required.
You can handle this i your code, if the variable is not present in json then in Offer object that variable will simple be assigned as null.
You can add null check to your code for the required fields and throw your own exception.
Since gson dont have this facility, you can also try the answer from below link-
Gson optional and required fields
To achieve this you need to follow two steps:-
1) Mark all required field in Offer class as #NotNull(message="your custom message")
2) Add below class to tell Mongo to validate document before persisting it to the database.
#Configuration
public class MongoEventValidationListener {
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}

How can I use a local variable in the annotation #Preauthorize?

i need to do something like this
String myVar = "myString";
...
#Preauthorize("customMethod(myVar)")
public void myMethod() {
...
}
but I'm failing at it. How can I do that? It says it cannot be resolved
EDIT:I'm decoupling few rest services and sometimes I have to share infos between them
#Value("${my-properties}")
String urlIWantToShare;
...
#PreAuthorize("isValid(#myValue,urlIWantToShare)")
#RequestMapping(value = "**/letsCheckSecurityConfig", method = RequestMethod.GET)
public boolean letsCheckSecurityConfig(#RequestHeader(name = "MY-VALUE") String myValue)) {
return true;
}
this "isValid" custom security method will call an external service, that doesn't know anything about the caller and his infos. I need to transmit few infos and I need to take them from different kind of sources
One of the sources is my application.properties
EDIT2: I managed to do this
#PreAuthorize("isValid(#myValue, #myProperty)")
#RequestMapping(value = "**/letsCheckSecurityConfig", method = RequestMethod.GET)
public boolean letsCheckSecurityConfig(#RequestHeader(name = "MY-VALUE") String myValue,
#Value("${my-property-from-app-properties}") String myProperty))
..but I want to use not only actual static properties but runtime one. Any help?
You can create a wrapper method without parameters which will call the desired method with parameters. In the annotation you can use the method without parameters
Apologies if I have misunderstood what you are trying to do, but from my understanding you're trying to set an annotation at runtime based on a variable / app.properties, so that you can then read this variable and then execute your class?
If this is the case, You cannot do this from an annotation alone as annotations cannot read local variables and cannot be set at runtime.
However, one option for you is to have an object which contains the 'values' of interest for you and then read the values from the object.
Something like the below:
PoJo
public class testObject{
#test
private String myVar;
private String myValue;
//Getters and Setters
}
Get Object values
public void getFields (Object obj){
Field fields = obj.getClass().getDeclaredFields();
for (Field f : fields){
test fieldAnnotation = f.getAnnotation(test.Class);
if (fieldAnnotation != null){
f.get(obj);
// Do checks based on this
}
}
}
Main Class
public static void main(String[] args){
//Create object
testObject test = new testObject();
test.setOne("testOne");
test.setTwo("testTwo");
getFields(test);
}
I've pulled this code based on what I had to do to get the fields - but in my case, I did not know the object types I was going to be passed. You are simply using the annotation to 'mark' the fields you want to retrieve and then reading the value from the object.
If you're in a similar situation, then you can see my answer here: initial answer
Let me know if i've misunderstood this and I can try and further clarify my answer.

My spring mvc session was changed implicitly after called a service method?

I'm using spring 3.2.5 via annotations and got some issue dealing with session.
My controller class is like this:
#Controller
public class WebController {
#Autowired
private IElementService elementService;
...
//in this method I set the "elementList" in session explicitly
#RequestMapping("/elementSearch.do")
public String elementSearch(
#RequestParam("keyword") String keyword,
HttpSession session){
List<Element> elementList= elementService.searchElement(keyword);
session.setAttribute("elementList", elementList);
return "searchResult";
}
//here I got my problem
#RequestMapping(value="/anotherMethod.do", produces="text/html; charset=utf-8")
#ResponseBody
public String anotherMethod(
...
//I called my service method here like
Element e = elementService.searchElement("something").get(0);
...
}
And I have a ElementServiceImpl class like this:
#Service
public class ElementServiceImpl implements IElementService {
#Autowired
private IBaseDAO baseDao;
#Override
public List<Metadata> searchElement(String keyword) {
List<Metadata> re = baseDao.searchElement(keyword);
return re;
}
}
And I have a BaseDAOImpl class implemented IBaseDAO and annonated with #Repository:
#Repository
public class BaseDAOImpl implements IBaseDAO {
...
}
Here is the problem, when I visit ".../anotherMethod.do", which will call the anotherMethod up there, my "elementList" in session was changed!
Then I looked into the anotherMethod() and found everytime
Element e = elementService.searchElement("something").get(0);
was called, my elementList was change to the new result returned by searchElement method(which returns a List).
But I didn't set session in that method, and I'm not using #SessionAttributes, so I don't understand how could my session attribute changed after calling a service method?
This problem is torturing me right now so any advise would be a great help, thanks!
update: I tried to print all my session attributes around that method call like this:
StringBuilder ss1 = new StringBuilder("-------------current session-------------\n");
session.setAttribute("test1", "test value 1");
log.info("sessionTest - key:test1 value:" + session.getAttribute("test"));
Enumeration<String> attrs1 = session.getAttributeNames();
while(attrs1.hasMoreElements()){
String key = attrs1.nextElement();
ss1.append(key).append(":").append(session.getAttribute(key)).append("\n");
}
log.info(ss1);
But I didn't see whether the "elementList" or the test value which I added just before print. And I do can get some value by
List<Element> elementList = (List<Element>) session.getAttribute("elementList");
and the elementList I get changed after calling service method, just like I posted before. Where my elementList stored? not in the session?
My goal is to show the elementList to the users in a table, and let them pick one of them, then I get the row number of the table and take it as a index of the elemntList, so I'll know which elemnt object the user picked. Are there any better way to do this so I can get rid of that problem?
Thanks again.

Spring MVC: how to indicate whether a path variable is required or not?

I am doing a Spring web. For a controller method, I am able to use RequestParam to indicate whether a parameter it is required or not. For example:
#RequestMapping({"customer"})
public String surveys(HttpServletRequest request,
#RequestParam(value="id", required = false) Long id,
Map<String, Object> map)
I would like to use PathVariable such as the following:
#RequestMapping({"customer/{id}"})
public String surveys(HttpServletRequest request,
#PathVariable("id") Long id,
Map<String, Object> map)
How can I indicate whether a path variable is required or not? I need to make it optional because when creating a new object, there is no associated ID available until it is saved.
Thanks for help!
VTTom`s solution is right, just change "value" variable to array and list all url possibilities: value={"/", "/{id}"}
#RequestMapping(method=GET, value={"/", "/{id}"})
public void get(#PathVariable Optional<Integer> id) {
if (id.isPresent()) {
id.get() //returns the id
}
}
There's no way to make it optional, but you can create two methods with one having the #RequestMapping({"customer"}) annotation and the other having #RequestMapping({"customer/{id}"}) and then act accordingly in each.
I know this is an old question, but searching for "optional path variable" puts this answer high so i thought it would be worth pointing out that since Spring 4.1 using Java 1.8 this is possible using the java.util.Optional class.
an example would be (note the value must list all the potential routes that needs to match, ie. with the id path variable and without. Props to #martin-cmarko for pointing that out)
#RequestMapping(method=GET, value={"/", "/{id}"})
public void get(#PathVariable Optional<Integer> id) {
if (id.isPresent()) {
id.get() //returns the id
}
}
VTToms answer will not work as without id in path it will not be matched (i.e will not find corresponding HandlerMapping) and consequently controller will not be hit. Rather you can do -
#RequestMapping({"customer/{id}","customer"})
public String surveys(HttpServletRequest request, #PathVariable Map<String, String> pathVariablesMap, Map<String, Object> map) {
if (pathVariablesMap.containsKey("id")) {
//corresponds to path "customer/{id}"
}
else {
//corresponds to path "customer"
}
}
You can also use java.util.Optional which others have mentioned but it requires requires Spring 4.1+ and Java 1.8..
There is a problem with using 'Optional'(#PathVariable Optional id) or Map (#PathVariable Map pathVariables) in that if you then try to create a HATEOAS link by calling the controller method it will fail because Spring-hateoas seems to be pre java8 and has no support for 'Optional'. It also fails to call any method with #PathVariable Map annotation.
Here is an example that demonstrates the failure of Map
#RequestMapping(value={"/subs","/masterclient/{masterclient}/subs"}, method = RequestMethod.GET)
public List<Jobs> getJobListTest(
#PathVariable Map<String, String> pathVariables,
#RequestParam(value="count", required = false, defaultValue = defaultCount) int count)
{
if (pathVariables.containsKey("masterclient"))
{
System.out.println("Master Client = " + pathVariables.get("masterclient"));
}
else
{
System.out.println("No Master Client");
}
//Add a Link to the self here.
List list = new ArrayList<Jobs>();
list.add(linkTo(methodOn(ControllerJobs.class).getJobListTest(pathVariables, count)).withSelfRel());
return list;
}
I know this is an old question, but as none of the answers provide some updated information and as I was passing by this, I would like to add my contribution:
Since Spring MVC 4.3.3 introduced Web Improvements,
#PathVariable(required = false) //true is default value
is legal and possible.
#RequestMapping(path = {"/customer", "/customer/{id}"})
public String getCustomerById(#PathVariable("id") Optional<Long> id)
throws RecordNotFoundException
{
if(id.isPresent()) {
//get specific customer
} else {
//get all customer or any thing you want
}
}
Now all URLs are mapped and will work.
/customer/123
/customer/1000
/customer - WORKS NOW !!

Spring / AOP: Best way to implement an activities log in the database

I have been going through some Spring / AOP tutorials and have somewhat familiarized myself with the related concepts.
Now coming to my requirements, I need to create an Activities Log implementation which will save the activities of a logged-in user in the DB which can range from applying for a service or creating new users in case of Admin users, etc. On invocation of any method having an annotation (say #ActivityLog), this information is to be persisted in the form of actorId, actionComment, actionTime, actedUponId, ... etc.
Now, if I create a POJO class (that maps to a ActivityLog table in the DB) and want to save this data from inside the Advice (preferably using the same transaction as the method, method uses #Transactional annotation), how do I actually populate the variables in this POJO?? I can probably get the actorId from the session object & actionTime can simply be new Date() but how about the dynamic values for actionComment / actedUponId?
Any help will be brilliant! (BTW, I have a requirement to not use Hibernate Interceptors.)
Here is a complete example:
#Aspect
#Component
public class WebMethodAuditor {
protected final Log logger = LogFactory.getLog(getClass());
public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
#Autowired
AuditRecordDAO auditRecordDAO;
#Before("execution(* com.mycontrollers.*.*(..))")
public void beforeWebMethodExecution(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Timestamp timestamp = new Timestamp(new java.util.Date().getTime());
// only log those methods called by an end user
if(principal.getUsername() != null) {
for(Object o : args) {
Boolean doInspect = true;
if(o instanceof ServletRequestDataBinder) doInspect = false;
if(o instanceof ExtendedModelMap) doInspect = false;
if(doInspect) {
if(o instanceof BaseForm ) {
// only show form objects
AuditRecord ar = new AuditRecord();
ar.setUsername(principal.getUsername());
ar.setClazz(o.getClass().getCanonicalName());
ar.setMethod(methodName);
ar.setAsString(o.toString());
ar.setAudit_timestamp(timestamp);
auditRecordDAO.save(ar);
}
}
}
}
}
}
If you are looking to get the actionComment and actedUponId from arguments to the annotated method (assuming they're both strings), you can add binding terms to your #Around pointcut like this:
#Around("#annotation(ActivityLog) && args(actionComment,actedUponId)")
public Object logActivity(ProceedingJoinPoint pjp,
String actionComment, String actedUponId) throws Throwable {
// ... get other values from context, etc. ...
// ... write to log ...
pjp.proceed();
}
The args binding in a pointcut can be used in partially-specified mode, in case there are other arguments about that you aren't interested in, and since the aspect is itself a bean, it can be wired into everything else that is going on in the normal way.
Note that if you're mixing declarative transaction management on the same method calls, you've got to get the order of aspects correct. That's done in part by making the aspect bean also implement the Spring Ordered interface, and by controlling the precedence of transactions through the order attribute to <tx:annotation-driven/>. (If that's impossible, you'll be forced to do clever things with direct transaction handling; that's a vastly more painful option to get right…)
You will be getting a reference to the org.aspectj.lang.JoinPoint in your advice.You can get the name of target method being executed with toShortString().You can have a loop-up/property file with the method-name=comments entries.This comments can be populated into the POJO.actionComment.method-name can be set to POJO.actedUponId.
I hope the advice should run within the same transaction, if the data-access method is adviced and the service method uses #Transactional.

Resources