Get to the grails domain object from the Shiro Subject - java

I'm doing a simple little grails app and decided on Shiro for signup/security and I've run into a (probably silly) problem.
I've generated the User (and Realm) class, and then extended the User to have a one-to-many association with Posts (ie the User can write, eg, blog entries, is the idea). But how do I get the Domain object from the Shiro subject?
I've tried the following:
def currentUser = SecurityUtils.getSubject()
def posts = Post.findByUser(currentUser)
But that gives me: "Message: No converter found capable of converting from type org.apache.shiro.web.subject.support.WebDelegatingSubject to type com.lordfoom.challengetrackr.User"
The domain classes are as follows:
class User {
String username
String passwordHash
static hasMany = [ roles: Role, permissions: String, posts: Post ]
static constraints = {
username(nullable: false, blank: false, unique: true)
}
}
class Post {
String title;
String body;
static belongsTo = [user:User]
static constraints = {
title(nullable:false, blank: false, unique: true)
user(unique:true)
}
}
Is there a simple way to get from the Shiro Subject to the currently logged in user's domain object? Or do I have to look it up somehow?
Any help appreciated.

If I am understanding this correctly, you just want to retrieve the user object for the user who is currently signed in, yes?
The way that I usually achieve this is by setting up a UserService containing two methods. Then I can implement getLocalUser() throughout the application.
import org.apache.shiro.SecurityUtils
class UserService {
/**
* for currently logged in user
*/
def getLocalUserId(){
def userName = SecurityUtils.subject?.principal
User.findByUsername(userName)
}
User getLocalUser(){
getLocalUserId()
}
}
Hope this helps.

Related

How do I return a serialized response for field data not stored in ES

class Address {
String address1, country, state, zip;
}
class Foo {
#Field(type = FieldType.Object)
Address work;
boolean workAddressSameAsHome;
#Field(type = FieldType.Object)
Address home;
}
I would like to return work value as home in the JSON response if workAddressSameAsHome=true, as the value will not be stored in ES. How do I make this work for a GET request /foo/<id>
Spring Data Elasticsearch does not offer any REST URLs, so I assume you are using Spring Data Rest on top.
BTW, your Foo class has no #Document and #Id property, I guess your real class has one.
What you can do in Spring Data Elasticsearch is to provide a bean that implements the AfterConvertCallback interface - available since Spring Data Elasticsearch 4.0:
#Component
public class SetWorkAddressAsHomeCallback implements AfterConvertCallback<Foo> {
#Override
public Foo onAfterConvert(Foo foo, Document document, IndexCoordinates indexCoordinates) {
if (foo.workAddressSameAsHome) {
foo.home = foo.work;
}
return foo;
}
}
This callback is invoked after the document is read from Elasticsearch. Here I just copy the work address to the home address field.
For more information about the callbacks check the documentation at https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.entity-callbacks
You might consider to implement a BeforeConvertCallback as well where you would clear the home address when the workAddressSameAsHome is true, this would be invoked before the entity is saved in Elasticsearch, thus preventing to store a home address in this case. But keep in mind that you won't be able to do an Elasticsearch search for the home address in this case.

How can I produce a graphql schema for federation with java?

My group is planning to use Apollo Gateway for federation. Therefore, we need to produce our schemas a little bit differently.
Can we produce something like this with using your amazing lib?
extend type User #key(fields: "id") {
id: ID! #external
reviews: [Review]
}
You want to add some fields and directives to a type?
You can use #GraphQLContext to attach external methods as fields. Or even provide a custom ResolverBuilder that returns additional Resolvers (these later get mapped to fields).
To add directives, you can create annotations meta-annotated with #GraphQLDirective (see the tests for examples).
Lastly, you can of course provide a custom TypeMapper for User and fully take control of how that type gets mapped.
E.g. you can make an annotation like:
#GraphQLDirective(locations = OBJECT)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Key {
public String[] fields;
}
If you then place this annotation on a type:
#Key(fields = "id")
public class User {
#External //another custom annotation
public #GraphQLId #GraphQLNonNull String getId() {...}
}
it will get mapped as:
type User #key(fields: "id") {
id: ID! #external
}
I presume you know about #GraphQLContext, but in short:
//Some service class registered with GraphQLSchemaBuilder
#GraphQLApi
public class UserService {
#GraphQLQuery
public List<Review> getReviews(#GraphQLContext User user) {
return ...; //somehow get the review for this user
}
}
Because of #GraphQLContext, the type User now has a review: [Review] field (even though the User class does not have that field).

Dynamic POJO validation based on groups in spring

Consider the following pojo for reference:
public class User{
private String username;
private String firstName;
private String middleName;
private String lastName;
private String phone;
//getters and setters
}
My application is a basically spring-boot based REST API which exposes two endpoints, one to create the user and the other to retrieve a user.
The "users" fall into certain categories, group-a, group-b etc. which I get from the headers of the post request.
I need to validated the user data in runtime and the validations may differ based on the group of a user.
for example, the users that fall into group-a may have phone numbers as an optional field whereas it might be a mandatory field for some other group.
The regex may also vary based on their groups.
I need to be able to configure spring, to somehow dynamically validate my pojo as soon as they are created and their respective set of validations get triggered based on their groups.
Maybe I can create a yml/xml configuration which would allow me to enable this?
I would prefer to not annotate my private String phone with #NotNull and #Pattern.
My configuration is as follows:
public class NotNullValidator implements Validator {
private String group;
private Object target;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
#Override
public void validate(Object o) {
if (Objects.nonNull(o)) {
throw new RuntimeException("Target is null");
}
}
}
public interface Validator {
void validate(Object o);
}
#ConfigurationProperties(prefix = "not-null")
#Component
public class NotNullValidators {
List<NotNullValidator> validators;
public List<NotNullValidator> getValidators() {
return validators;
}
public void setValidators(List<NotNullValidator> validators) {
this.validators = validators;
}
}
application.yml
not-null:
validators:
-
group: group-a
target: user.username
-
group: group-b
target: user.phone
I want to configure my application to somehow allow the validators to pick their targets (the actual objects, not the strings mentioned in the yml), and invoke their respective public void validate(Object o) on their targets.
P.S.
Please feel free to edit the question to make it better.
I am using jackson for serializing and deserializing JSON.
The easiest solution to your problem, as i see it, is not with Spring or the POJOs themselves but with a design pattern.
The problem you're describing is easily solved by a strategy pattern solution.
You match the strategy to use by the header you're expecting in the request, that describes the type of user, and then you perform said validations inside the strategy itself.
This will allow you to use the same POJO for the whole approach, and deal with the specifics of handling/parsing and validating data according to the each type of user's strategy.
Here's a link from wiki books with a detailed explanation of the pattern
Strategy Pattern
Suppose you have a basic interface for your strategies:
interface Strategy {
boolean validate(User user);
}
And you have 2 different implementations for the 2 different types of user:
public class StrategyA implements Strategy {
public boolean validate(User user){
return user.getUsername().isEmpty();
}
}
public class StrategyB implements Strategy {
public boolean validate(User user){
return user.getPhone().isEmpty();
}
}
You add a Strategy attribute to your User POJO and assign the right implementation of the Strategy to that attribute when you receive the post request.
Everytime you need to validate data for that user you just have to invoke the validate method of the assigned strategy.
If each User can fit multiple strategies, you can add a List<Strategy> as an attribute instead of a single one.
If you don't want to change the POJO you have to check which is the correct strategy every time you receive a post request.
Besides the validate method you can add methods to handle data, specific to each strategy.
Hope this helps.
You can use validation groups to control which type of user which field gets validated for. For example:
#NotBlank(groups = {GroupB.class})
private String phone;
#NotBlank(groups = {GroupA.class, GroupB.class})
private String username;
Then you use the headers from the request that you mentioned to decide which group to validate against.
See http://blog.codeleak.pl/2014/08/validation-groups-in-spring-mvc.html?m=1 for a complete example.
Updated to include a more comprehensive example:
public class Val {
private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public boolean isValid(User user, String userType) {
usergroups userGroup = usergroups.valueOf(userType);
Set<ConstraintViolation<User>> constraintViolations = validator.validate(user, userGroup.getValidationClass());
return constraintViolations.isEmpty();
}
public interface GroupA {}
public interface GroupB {}
public enum usergroups {
a(GroupA.class),
b(GroupB.class);
private final Class clazz;
usergroups(Class clazz) {
this.clazz = clazz;
}
public Class getValidationClass() {
return clazz;
}
}
}
This doesn't use application.yaml, instead the mapping of which fields are validated for each group is set in annotations, similar results using Spring's built in validation support.
I was able to solve my problem with the use of Jayway JsonPath.
My solution goes as follows:
Add a filter to your API which has the capability to cache the InputStream of the ServletRequest since it can be read only once. To achieve this, follow this link.
Create a bunch of validators and configure them in your application.yml file with the help of #ConfigurationProperties. To achieve this, follow this link
Create a wrapper which would contain all your validators as a list and initialize it with #ConfigurationProperties and the following configuration:
validators:
regexValidators:
-
target: $.userProfile.lastName
pattern: '[A-Za-z]{0,12}'
group: group-b
minMaxValidators:
-
target: $.userProfile.age
min: 18
max: 50
group: group-b
Call the validate method in this wrapper with the group which comes in the header, and then call the validate of the individual validators. To achieve this, I wrote the following piece of code in my wrapper:
public void validate(String input, String group) {
regexValidators.stream()
.filter(validator -> group.equals(validator.getGroup()))
.forEach(validator -> validator.validate(input));
minMaxValidators.stream()
.filter(validator -> group.equals(validator.getGroup()))
.forEach(validator -> validator.validate(input));
}
and the following method in my validator:
public void validate(String input) {
String data = JsonPath.parse(input).read(target);
if (data == null) {
throw new ValidationException("Target: " + target + " is NULL");
}
Matcher matcher = rule.matcher(data);
if (!matcher.matches()) {
throw new ValidationException("Target: " + target + " does not match the pattern: " + pattern);
}
}
I have created a functioning project to demonstrate the validations and it can be found here.
I understand that the answer alone might not be very clear, please follow the above mentioned url for the complete source code.

Security - Jackson Password Serialization

I am using Jackson to serialize objects to be saved on MongoDB (through Jongo). These objects contain a password hash that I want to store on the database.
I also have a REST API that will return those objects. When the objects are serialized through the REST API they will contain the password hash. Despite the communication being done over HTTPS, this sounds like a security risk to me. How can I prevent the serialization of the password hash through the REST API but not for database persistence? Is there something like conditional serialization of fields?
#JsonView may suit your need.
// View definitions:
class Views {
static class Public { }
static class Internal extends Public { }
}
public class User {
// Name is public
#JsonView(Views.Public.class) String name;
// Hash password only for internal usage
#JsonView(Views.Internal.class) String hashPassword;
}
In your REST API, you could specify:
public class Resource {
#JsonView(Views.Public.class)
#GET
#Produces(MediaType.APPLICATION_JSON )
public List< User > getElements() {
//do something
return someResultList;
}
}
The above api would only include "name" property of User in the response.
Note: If no view annotation, assumed to mean View identified by Object.class: that is, included in all views.
When serializing to DB, you could do this:
objectMapper.viewWriter(Views.Internal.class).writeValue(out, beanInstance);
Which would include all properties of User.
More info here: http://wiki.fasterxml.com/JacksonJsonViews
use this annotation on the password field in the object:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)

JSR-303 / Spring MVC - validate conditionally using groups

I worked out a concept to conditionally validate using JSR 303 groups. "Conditionally" means that I have some fields which are only relevant if another field has a specific value.
Example: There is an option to select whether to register as a person or as a company. When selecting company, the user has to fill a field containing the name of the company.
Now I thought I use groups for that:
class RegisterForm
{
public interface BasicCheck {}
public interface UserCheck {}
public interface CompanyCheck {}
#NotNull(groups = BasicCheck.class)
private Boolean isCompany
#NotNull(groups = UserCheck.class)
private String firstName;
#NotNull(groups = UserCheck.class)
private String lastName;
#NotNull(groups = CompanyCheck.class)
private String companyName;
// getters / setters ...
}
In my controller, I validate step by step depending on the respective selection:
#Autowired
SmartValidator validator;
public void onRequest(#ModelAttribute("registerForm") RegisterForm registerForm, BindingResult result)
{
validator.validate(registerForm, result, RegisterForm.BasicCheck.class);
if (result.hasErrors()
return;
// basic check successful => we can process fields which are covered by this check
if (registerForm.getIsCompany())
{
validator.validate(registerForm, result, RegisterForm.CompanyCheck.class)
}
else
{
validator.validate(registerForm, result, RegisterForm.UserCheck.class);
}
if (!result.hasErrors())
{
// process registration
}
}
I only want to validate what must be validated. If the user selects "company" fills a field with invalid content and then switches back to "user", the invalid company related content must be ignored by the validator. A solution would be to clear those fields using Javascript, but I also want my forms to work with javascript disabled. This is why I totally like the approach shown above.
But Spring breaks this idea due to data binding. Before validation starts, Spring binds the data to registerForm. It adds error to result if, for instance, types are incompatible (expected int-value, but user filled the form with letters). This is a problem as these errors are shown in the JSP-view by <form:errors /> tags
Now I found a way to prevent Spring from adding those errors to the binding result by implementing a custom BindingErrorProcessor. If a field contains null I know that there was a validation error. In my concept null is not allowed - every field gets annotated with #NotNull plus the respective validation group.
As I am new to Spring and JSR-303 I wonder, whether I am totally on the wrong path. The fact that I have to implement a couple of things on my own makes me uncertain. Is this a clean solution? Is there a better solution for the same problem, as I think this is a common problem?
EDIT
Please see my answer here if you are interested in my solution in detail: https://stackoverflow.com/a/30500985/395879
You are correct that Spring MVC is a bit picky in this regard,and it is a common problem. But there are work-arounds:
Make all your backing fields strings, and do number/date etc conversions and null checks manually.
Use JavaScript to set fields to null when they become irrelevant.
Use JavaScript to validate fields when they are entered. This will fix almost all of your problems.
Good luck!
I know this question is old, but I came upon it looking for an answer for a different situation.
I think for your situation you could use inheritance for the forms and then use two controller methods:
The forms would look like this:
public class RegistrationForm
{
// Common fields go here.
}
public class UserRegistrationForm
extends RegistrationForm
{
#NotNull
private String firstName;
#NotNull
private String lastName;
// getters / setters ...
}
public class CompanyRegistrationForm
extends RegistrationForm
{
#NotNull
private String companyName;
// getters / setters ...
}
The controller methods would look like this:
#RequestMapping(method = RequestMethod.POST, params = "isCompany=false")
public void onRequest(
#ModelAttribute("registerForm") #Valid UserRegistrationForm form,
BindingResult result)
{
if (!result.hasErrors())
{
// process registration
}
}
#RequestMapping(method = RequestMethod.POST, params = "isCompany=true")
public void onRequest(
#ModelAttribute("registerForm") #Valid CompanyRegistrationForm form,
BindingResult result)
{
if (!result.hasErrors())
{
// process registration
}
}
Notice that the #RequestMapping annotations include a params attribute so the value of the isCompany parameter determines which method is called.
Also notice that the #Valid annotation is place on the form parameter.
Finally, no groups are needed in this case.

Categories

Resources