Cannot set user roles with enum on Spring Boot - java

I was following a tutorial (https://www.youtube.com/watch?v=TOox3CGarf8) on how to implement JWT authentication on Spring, and it all worked well, that is, until I had to implement the very same InitUsers.java shown in the video.
The problem I'm getting is the following (inside .role(Set.of(Role.ROLE_ADMIN, Role.ROLE_USER) from InitUsers.java, which is shown further below):
Required type: Set<management.relation.Role>
Provided: Set<[path].user.Role>
no instance(s) of type variable(s) exist so that Role conforms to Role inference variable E has incompatible bounds: equality constraints: Role lower bounds: Role
I'm not too well versed in Java, so I'm wondering how I can apply the necessary changes for the code to work, without breaking it and straying away from what is shown in the video.
The relevant implementations are below:
InitUsers.java
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import [path].user.JwtUser;
import [path].user.JwtUserService;
import [path].user.Role;
import java.util.Set;
#Component
#RequiredArgsConstructor
public class InitUsers implements CommandLineRunner {
private final JwtUserService jwtUserService;
private final PasswordEncoder passwordEncoder;
#Override
public void run(String... args) throws Exception {
if (jwtUserService.findJwtUserByEmail("emailaaaa#gmail.com").isEmpty()) {
JwtUser u = jwtUserService.save(JwtUser.builder()
.username("Admin")
.email("emailaaaa#gmail.com")
.password(passwordEncoder.encode("testpwd"))
.role(Set.of(Role.ROLE_ADMIN, Role.ROLE_USER))
.build());
u.setEnabled(true);
jwtUserService.save(u);
}
if (jwtUserService.findJwtUserByEmail("someemail#gmail.com").isEmpty()) {
JwtUser u = jwtUserService.save(JwtUser.builder()
.username("Someone")
.email("someemail#gmail.com")
.password(passwordEncoder.encode("123"))
.role(Set.of(Role.ROLE_USER))
.build());
u.setEnabled(true);
jwtUserService.save(u);
}
}
}
Roles.java
public enum Role {
ROLE_USER,
ROLE_ADMIN
}

It turns out I was tricked by IntelliJ's imports. In my JwtUser class, I had the following:
#Column
#Enumerated(EnumType.STRING)
fetch = FetchType.EAGER)
Set<Role> role = new HashSet<>();
The Role there was mistakenly imported from javax.management.Role or something of the like, and not my own implementation in the same package. This was a little frustrating but it serves as a nice lesson - remember to double check your imports!

Related

How to turn off deserialization for enum

I would like to turn off deserialization for concrete enum. Is it possible?
Exercise model class:
package main.exercise;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Entity
#Builder
public class Exercise {
#Id
#GeneratedValue
private int id;
#NonNull
private String name;
#NonNull
private ExerciseType exerciseType;
private double caloriesBurned;
private String exerciseDescription;
}
I got method in controller:
#PostMapping("/addExercise")
public List<String> addExercise(#RequestBody Exercise exercise) {
return exerciseCrudActivitiesService.addExercise(exercise);
}
which takes Exercise body and if type of Exercise is wrong I got error while POST http request:
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `main.exercise.ExerciseType` from String "CARdDIO": not one of the values accepted for Enum class: [CARDIO, WEIGHTLIFTING]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `main.exercise.ExerciseType` from String "CARdDIO": not one of the values accepted for Enum class: [CARDIO, WEIGHTLIFTING]
at [Source: (PushbackInputStream); line: 4, column: 25] (through reference chain: main.exercise.Exercise["exerciseType"])]
The point is in service I got validator, which validates whether type of enum is right or wrong and return string to list of errors from all validators. Unfortunately this code cannot be reached because of error.
public List<String> addExercise(Exercise exercise) {
ExerciseValidator validator = new ExerciseValidator();
List<String> messages = validator.validate(exercise);
if (messages.isEmpty()) {
exerciseRepository.save(exercise);
}
return messages;
}
Validator
package main.exercise.validator.attributesvalidators;
import main.exercise.Exercise;
import main.exercise.ExerciseType;
import main.exercise.validator.ExerciseAttributesValidator;
import java.util.InputMismatchException;
public class ExerciseTypeValidator implements ExerciseAttributesValidator {
#Override
public String validate(Exercise exercise) {
if (exercise.getExerciseType() == null) {
return "You didn't put exercise type!";
}
try {
ExerciseType.forName(exercise.getExerciseType().name());
} catch (InputMismatchException e) {
return "Wrong exercise type!";
}
return null;
}
}
To turn off (de-)serialization, you can add the #JsonIgnore to the exerciseType field. However, I don't think this will help you anyways.
If serialization is ignored, the field would always be null which is not the intended behavior.
Your validator is too late. Note: the validate method takes an Exercise object as a parameter. The problem occurs during the creation of this object already.
When you get to the point that the line ExerciseType.forName(exercise.getExerciseType().name()); get's executed, it will NEVER throw an exception, because getExerciseType() is already a valid enum.
Instead of this custom validator, you could make use of a Spring #ControllerAdvice to register your own Exception handler for that error type.
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import com.fasterxml.jackson.databind.exc.InvalidFormatException
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(InvalidFormatException.class)
public ResponseEntity<?> badFormatException(InvalidFormatException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
See e.g. https://www.springboottutorial.com/spring-boot-exception-handling-for-rest-services for more details.

Is it possible to to do validation checking at run time other than compile time?

With the following Java code:
public class Bean{
private String value;
public Bean(#NonNull String value) {
//Usually fail-fast validation can be added here if it is needed
this.value = value;
}
public String getValue() {return this.value;}
}
Is it possible to check the constructor argument value by means of the annotation, #NonNull at run time other than compile time? Personally I still did not find any checker-framework, which can do validation checking at run time. However, is it possible to implement an Annotation processor to do run time checking?
You should take a look at #NotNull from javax.validation.constraints.
I use it in my models and it throw a Constraint exception when I try to save a model with a null #NotNull value.
The import is import javax.validation.constraints.NotNull;
If you are using Spring and mongodb, you'll have to configure it so it works, I have found a piece of code somewhere on the Internet (can't remember where), you may use it:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
#Configuration
public class CustomRepositoryRestConfigurerAdapter {
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
#Bean
public ValidatingMongoEventListener validatingMongoEventListener(
#Qualifier("localValidatorFactoryBean") LocalValidatorFactoryBean lfb
) {
return new ValidatingMongoEventListener(lfb);
}
}
Yes. Lombok's #NonNull is a runtime check which just inserts an if-statement with a throw:
With Lombok
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(#NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
Vanilla Java
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(#NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked #NonNull but is null");
}
this.name = person.getName();
}
}
Misconception at your end: there is no single answer to your question.
Some annotations, when used on source code like this are mainly targeting compile time. Like some static analysis tool that analyses the data flow to tell you that you are violating "annotated" contracts here or there.
But some annotations are also "meant" to be used at runtime, for example to be used with "beans". Such objects might come in as parameter of a HTTP request, and then you have some framework checking if the content received as JSON for example is actually valid, according to the rules specified via annotations. See this tutorial for some examples.

Clashes between count() methods - CrudRepository - Spring

I prefer to warn you, my english is not perfect but I'll try to do my best.
I'm actually in an internship and my task is to create a webservice. Before this, I'm supposed to work with Maven and create repositories, models and others.
For now, I've got a problem with the repository of a simple Java class.
Here is my class :
package com.XXX;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.sql.Timestamp;
#Entity
//Generate getters and setters
#Data
//Generate a constructor with to arguments
#NoArgsConstructor
public class Day_ {
#Id
private int day_id_Date;
private Timestamp day_Date;
public Day_(int day_id_Date, Timestamp day_Date) {
this.day_id_Date = day_id_Date;
this.day_Date = day_Date;
}
}
Pretty simple as I said.
Now here comes my repository :
package com.XXX;
import com.XXX.Day_;
import org.springframework.data.repository.CrudRepository;
public interface DayRepository extends CrudRepository<Day_, Long> {
}
I've actually took this from an example on internet. Works fine for him but not for me. I'm getting the following error :
Error:(6, 8) java: types org.springframework.data.repository.Repository<com.atos.test.account.tables.Day_,java.lang.Long> and org.springframework.data.repository.CrudRepository<com.atos.test.account.tables.Day_,java.lang.Long> are incompatible; both define count(), but with unrelated return types
Now I've tried to override the count() method by doing :
package com.XXX;
import com.XXX.Day_;
import org.springframework.data.repository.CrudRepository;
public interface DayRepository extends CrudRepository<Day_, Long> {
#Override
long count();
}
But I get the following error (which is almost the same) :
Error:(6, 8) java: types org.springframework.data.repository.CrudRepository<com.atos.test.account.tables.Day_,java.lang.Long> andorg.springframework.data.repository.Repository<com.atos.test.account.tables.Day_,java.lang.Long> are incompatible; both define count(), but with unrelated return types
Error:(9, 10) java: count() in com.atos.test.account.repository.DayRepository clashes with count() in org.springframework.data.repository.Repository return type long is not compatible with java.lang.Long
I've looked into the CrudRepository class but the methods count() is the same than the one I tried. I've also looked into the Repository class because CrudRepository extends it but there is no method count.
EDIT
So I think I've resolved the problem :
Instead of extending my interface to CrudRepositoryI extended it to Repository. The thing is that I don't know if I can use the same methods ther is in CrudRepository, can I ?
You need to return type "Long".
#Override
Long count();

How to return a list of custom objects on Objectify

I'm working on an Android project which uses Google App Engine for backend as described here: Using Android & Google App Engine on Android Studio.
I have some model classes on the backend side like User and Item, and I'm trying to return a list of Items user has.
public List<Ref<Item>> getItems() {
return items;
}
When I try to Sync Project with Gradle Files, I get this error:
Error:Execution failed for task ':backend:appengineEndpointsGetClientLibs'.
There was an error running endpoints command get-client-lib: Parameterized type com.googlecode.objectify.Ref≤backend.model.Item> not supported.
I checked some other questions here and was able to build the project without errors by adding #ApiResourceProperty(ignored = AnnotationBoolean.TRUE) annotation to my getter method. But after adding this line, I cannot see this method on Android app side.
Any idea how to make it possible to get a list of Items on Android side?
I did it by saving/retrieving object that contains serialized collection. Class Lesson implements Serializable.
Language.java
import java.io.Serializable;
import java.util.List;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Serialize;
#Entity
public class Language {
#Id
private String key;
private String title;
#Serialize
private List<Lesson> lessons; //here collection
//getters/setters ommited
}
LanguageService.java
import static com.googlecode.objectify.ObjectifyService.ofy;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.Named;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import com.minspok.entity.Language;
#Api(name = "langapi", version = "v1", description = "langapi")
public class LanguageService {
static{
ObjectifyService.register( Language.class );
}
#ApiMethod(name = "get")
public Language getLanguage(#Named("key") String key){
Language language = ofy().load().key(Key.create(Language.class,
key)).now();
return language;
}
#ApiMethod(name = "create")
public void createLanguage(Language language){
ofy().save().entity(language);
}
}
Helpful reading: https://github.com/objectify/objectify/wiki/Entities

Java custom annotation

I want to write custome annonation on method .my usecase is - I have user which has role so when user try to access some method of class then i want to check his roles. so roles i want pass in annotation.
my code is here
package com.samples;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Roles {
public String roles();
}
package com.samples;
public class AnnotationExample {
#Roles(roles="admin")
public void createAction(){
String userrole = "admin"; // this hardcoded value
// a
// here i want to compare annotaiton value with user role
}
}
How can i do this ?
It is possible to declare annotation value as object value. like
#Roles(Role.AADMIN)
To check the annotations you need to use reflection.
Roles r = AnnotationExample.class.getMethod("createAction").getAnnotation(Roles.class);
if ("admin".equals(r.roles())) //has admin roles
It is possible to declare annotation value as object value. like #Roles(Role.AADMIN)
I think what you want is a Enum.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Roles {
public Role roles();
}
enum Role { ADMIN, USER };
public class AnnotationExample {
#Roles(roles=Role.ADMIN)
public void createAction(){
Role userrole = Role.ADMIN;
}
}
If you really need to reinvent security annotations (if you don't want to use already available options with declarative security like EJB or Spring) you may at least want to see how they implement it. Here for example for one of EJB implementations.
But maybe you would reconsider?

Categories