I'm trying to use different objects for different user roles.
#Component
public class TestObject {
private String name;
private String secret;
#PreAuthorize("hasRole('ADMIN')")
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Is there a way to use #PreAuthorize like this?
You can use #PreAuthorize only in a Spring Managed Bean.
If you have to create multiple instances of an object, these instances are not managed by the application context.
If you mark a class with #Component, this becomes a Spring bean and can be injected and managed by the application context. Now, if you (not Spring) create a instance of this class, then this specific instance isn't a managed by Spring.
This means, that #PreAuthorize will not work in this instance.
One workaround is to use the SecurityContextHolder.
Something like this:
Utils.java
public class Utils {
public static boolean isAdmin() {
Collection<?extends GrantedAuthority> authorities = SecurityContextHolder.getContext()
.getAuthentication().getAuthorities();
for (GrantedAuthority authority: authorities) {
if (authority.getAuthority().equalsIgnoreCase("ADMIN")) {
return true;
}
}
return false;
}
}
TestObject.java
public class TestObject {
private String name;
private String secret;
public String getSecret() {
if(Utils.isAdmin())
return secret;
return null;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Related
I have a spring-boot application and I use DTO like that:
Service
#Service
public class UnitOfMeasureServiceImpl implements IUnitOfMeasureService {
private final IUnitsOfMeasureRepository unitOfMeasureRepository;
#Autowired
public UnitOfMeasureServiceImpl(IUnitsOfMeasureRepository unitOfMeasureRepository) {
this.unitOfMeasureRepository = unitOfMeasureRepository;
}
#Override
public UnitOfMeasureDTO getUnitOfMeasureById(UUID id) {
Optional<UnitOfMeasure> optionalUnitOfMeasure = unitOfMeasureRepository.findById(id);
if (!optionalUnitOfMeasure.isPresent()){
// throw new ComponentNotFoundException(id);
return null;
}
return UnitOfMeasureDTO.factory(optionalUnitOfMeasure.get());
}
dto:
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UnitOfMeasureDTO {
private String id;
private String name;
private String description;
private String sourceInfoCompanyName;
private String originalId;
public static UnitOfMeasureDTO factory(UnitOfMeasure unitOfMeasure) {
UnitOfMeasureDTO dto = new UnitOfMeasureDTO();
dto.id = unitOfMeasure.getId().toString();
dto.name = unitOfMeasure.getName();
dto.description = unitOfMeasure.getDescription();
dto.sourceInfoCompanyName = unitOfMeasure.getSourceInfo().getSourceCompany().getName();
dto.originalId = unitOfMeasure.getOriginalId();
return dto;
}
}
controller:
#RestController
#RequestMapping(UnitOfMeasureController.BASE_URL)
public class UnitOfMeasureController {
public static final String BASE_URL = "/api/sust/v1/unitOfMeasures";
private final IUnitOfMeasureService unitOfMeasureService;
public UnitOfMeasureController(IUnitOfMeasureService unitOfMeasureService) {
this.unitOfMeasureService = unitOfMeasureService;
}
#GetMapping(path = "/{id}")
#ResponseStatus(HttpStatus.OK)
public UnitOfMeasureDTO getUnitOfMeasureDTO(#PathVariable("id") UUID id) {
UnitOfMeasureDTO unitOfMeasureDTO = unitOfMeasureService.getUnitOfMeasureById(id);
return unitOfMeasureDTO;
}
So in my service I have getUnitOfMeasureById(UUID id) that return a UnitOfMeasureDTO.
Now I need to call, from another service, getUnitOfMeasureById(UUID id) that return the domain entity UnitOfMeasure. I think it's correct to call a service method from another service (not a controller method!) and the separation between business logic is at the service layer. So is it correct to have 2 methods: getUnitOfMeasureDTOById and getUnitOfMeasureById in the service? (getUnitOfMeasureDTOById call getUnitOfMeasureById to avoid code duplication)
is it posible to generate a custom "presence checking" method name, being a method of the property itself rather the owning object?
I know I can use hasProperty() methods to check for presence of a value...
https://mapstruct.org/documentation/stable/reference/html/#source-presence-check
but with Optional or JsonNullable (from OpenApi nonullable) that checking method is on the property itself, not on the owning object... :-(
I can map JsonNullable or Optional easyly 'using' or extending a simple custom Mapper
#Mapper
public class JsonNullableMapper {
public <T> T fromJsonNullable(final JsonNullable<T> jsonNullable) {
return jsonNullable.orElse(null);
}
public <T> JsonNullable<T> asJsonNullable(final T nullable) {
return nullable != null ? JsonNullable.of(nullable) : JsonNullable.undefined();
}
}
what I would like to achieve is something like this as "presence check":
if(source.getProperty().isPresent()) {
target.set(customMapper.map(source.getProperty()));
}
Any one found a solution for this?
Thanks and regards
I have managed to implement custom lombok extension which generates "presence checknig" methods.
Here is an example project. In short I added #PresenceChecker annotation and implemented Lombok Javac Annotation handler.
It's possible to use it together with other Lombok annotations:
#Getter
#Setter
public class User {
private String name;
}
#Getter
#Setter
#PresenceChecker
public class UserUpdateDto {
private String name;
}
//MapStruct Mapper interface declaration
#Mapper
public interface UserMapper {
void updateUser(UserUpdateDto dto, #MappingTarget User user);
}
Generated code:
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
//MapStruct Mapper implementation
public class UserMapperImpl implements UserMapper {
#Override
public void updateUser(UserUpdateDto dto, User user) {
if ( dto == null ) {
return;
}
if ( dto.hasName() ) {
user.setName( dto.getName() );
}
}
}
The answer is unfortunately a straight no.
It is not possible in the current version of MapStruct (1.3.1final) and its not on the shortlist for 1.4.0. You could open up an issue on the git repo of MapStruct as feature request.
This little project follows a basic MVC pattern, i'm using spring boot and apache derby as an embedded data base.
1) When adding a hardcoded object list inside service class, they all share the same id. Is there an explanation for this behavior ?
This shows the problem (Don't mind the 'kkk' objects, i've solved that part already)
Screen1
So this is the object account i'm working with :
#Entity
public class Account {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String owner;
private double budget;
private double budgetInvest;
private double budgetFonction;
public Account() {
}
public Account(String owner, double budget, double budgetInvest, double budgetFonction
) {
this.owner=owner;
this.budget = budget;
this.budgetInvest = budgetInvest;
this.budgetFonction = budgetFonction;
}
public Account (String owner, double budget) {
this.owner = owner;
this.budget=budget;
}
public Account (String owner) {
this.owner=owner;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public double getBudget() {
return budget;
}
public void setBudget(double budget) {
this.budget = budget;
}
public double getBudgetInvest() {
return budgetInvest;
}
public void setBudgetInvest(double budgetInvest) {
this.budgetInvest = budgetInvest;
}
public double getBudgetFonction() {
return budgetFonction;
}
public void setBudgetFonction(double budgetFonction) {
this.budgetFonction = budgetFonction;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
These are the lines responsible for displaying the objects inside the view :
<tr th:each="account : ${accounts}">
<td th:text="${account.id}">id</td>
<td><a href="#" th:text="${account.owner}">Title
...</a></td>
<td th:text="${account.budget}">Text ...</td>
</tr>
Here is the controller :
#Controller
public class AccountController {
#Autowired
private AccountService accountService;
#RequestMapping(value="/", method=RequestMethod.GET)
public String index() {
return "index";
}
#RequestMapping(value="/accountAdd", method=RequestMethod.GET)
public String addAccount(Model model) {
model.addAttribute("account", new Account());
return "accountAdd";
}
#RequestMapping(value="/accountAdd", method=RequestMethod.POST)
public String postAccount(#ModelAttribute Account account) {
accountService.addAccount(account);
return "redirect:listAccount";
}
#RequestMapping(value="/listAccount", method=RequestMethod.GET)
public String listAccount(Model model) {
System.out.println(accountService.getAllAccounts());
model.addAttribute("accounts",accountService.getAllAccounts());
return "listAccount";
}
}
And finally the service class :
#Service
public class AccountService {
#Autowired
private AccountRepository accountRepository;
public List<Account> getAllAccounts(){
List<Account>accounts = new ArrayList<>(Arrays.asList(
new Account("Maths Department",1000000,400000,600000),
new Account("Physics Department",7000000,200000,500000),
new Account("Science Department",3000000,700000,1000000)
));
accountRepository.findAll().forEach(accounts::add);
return accounts;
}
public Account getAccount(long id) {
return accountRepository.findById(id).orElse(null);
}
public void addAccount(Account account) {
accountRepository.save(account);
}
public void updateAccount(long id, Account account) {
accountRepository.save(account);
}
public void deleteAccount(long id) {
accountRepository.deleteById(id);
}
}
Ok, so while i haven't yet found the exact answer as to why it affects the same id for every object in a static list.
I found an elegant workaround to not only solve the issue but also enhance the structure of the code.
Instead of doing whatever barbaric initialization I was trying to perform, It's way better to do this inside the main class :
#SpringBootApplication
public class PayfeeApplication {
#Autowired
private AccountRepository accountRepository;
public static void main(String[] args) {
SpringApplication.run(PayfeeApplication.class, args);
}
#Bean
InitializingBean sendDatabase() {
return () -> {
accountRepository.save(new Account("Maths Department",1000000,400000,600000));
accountRepository.save(new Account("Physics Department",7000000,200000,500000));
accountRepository.save(new Account("Science Department",3000000,700000,1000000));
};
}
}
Hi,
I have a problem that is very confusing for me because the mapping should work and it looks like it does map when the Spring Boot is started in debug mode. I don't know where else I can check for an obvious solution to this problem.
Here is the application.properties:
server.port=8082
server.contextPath = /
Here is the SpringBootInitializer class that adds a further "/api" to the >Servlet registration:
public class App extends SpringBootServletInitializer {
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
final ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/api/*");
final Map<String, String> params = new HashMap<String, String>();
params.put("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
params.put("contextConfigLocation", "org.spring.sec2.spring");
params.put("dispatchOptionsRequest", "true");
registration.setInitParameters(params);
registration.setLoadOnStartup(1);
return registration;
}
//
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.initializers(new MyApplicationContextInitializer()).sources(App.class);
}
public static void main(final String... args) {
new SpringApplicationBuilder(App.class).initializers(new MyApplicationContextInitializer()).run(args);
}
}
Here is the Controler which adds a further "users" to the mapping. The method >which I have set a debug point is the findAll and requires no futher mapping to >get to it (i.e. the root of /users/:
#Controller
#RequestMapping(value = users)
public class UserController extends AbstractController<User> {
#Autowired
private IUserService userService;
public UserController() {
super(User.class);
}
// API
// find
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public void getItsWorking() {
System.out.println("It's Working!!!");
}
}
Here is the User entity:
#Entity
public class User implements IEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private Long user_id;
#Column(name = "username", unique = true, nullable = false)
private String name;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private Boolean locked;
public User() {
super();
}
public User(final String nameToSet, final String passwordToSet, /*final
Set<Role> rolesToSet,*/ final Boolean lockedToSet) {
super();
name = nameToSet;
password = passwordToSet;
locked = lockedToSet;
}
// API
public Long getId() {
return user_id;
}
public void setId(final Long idToSet) {
user_id = idToSet;
}
public String getName() {
return name;
}
public void setName(final String nameToSet) {
name = nameToSet;
}
public String getEmail() {
return email;
}
public void setEmail(final String emailToSet) {
email = emailToSet;
}
public String getPassword() {
return password;
}
public void setPassword(final String passwordToSet) {
password = passwordToSet;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(final Boolean lockedToSet) {
locked = lockedToSet;
}
}
Here is the output on my Spring Boot debug when it starts up:
Mapped "{[/users],methods=[GET]}" onto public
java.util.List<org.um.persistence.model.User>
org.um.web.controller.UserController.findAll(javax.servlet.http.HttpServletRequest)
So, it looks like it is mapping correctly, but when I hit it using the Rest >Client tool add on in Firefox, I get the following when doing a "GET" on the >following url: http://localhost:8082/api/users using Content-Type: application/json in my header .
What is going on? Very confused.
You should put a #RequestMapping("/api") on you class, and a #RequestMapping("/users") on your method (that should preferably return something to the client).
This ways your endpoint will be exposed as /api/users and you will be able to easily add further endpoints under /api/* into this class.
I am developing a small cqrs implementation and I am very new to it.
I want to segregate each handlers(Command and Event) from aggregate and
make sure all are working well. The command handler are getting triggered
from controller but from there event handlers are not triggered. Could
anyone Please help on this.
public class User extends AbstractAnnotatedAggregateRoot<String> {
/**
*
*/
private static final long serialVersionUID = 1L;
#AggregateIdentifier
private String userId;
private String userName;
private String age;
public User() {
}
public User(String userid) {
this.userId=userid;
}
#Override
public String getIdentifier() {
return this.userId;
}
public void createuserEvent(UserCommand command){
apply(new UserEvent(command.getUserId()));
}
#EventSourcingHandler
public void applyAccountCreation(UserEvent event) {
this.userId = event.getUserId();
}
}
public class UserCommand {
private final String userId;
public UserCommand(String userid) {
this.userId = userid;
}
public String getUserId() {
return userId;
}
}
#Component
public class UserCommandHandler {
#CommandHandler
public void userCreateCommand(UserCommand command) {
User user = new User(command.getUserId());
user.createuserEvent(command);
}
}
public class UserEvent {
private final String userId;
public UserEvent(String userid) {
this.userId = userid;
}
public String getUserId() {
return userId;
}
}
#Component
public class UserEventHandler {
#EventHandler
public void createUser(UserEvent userEvent) {
System.out.println("Event triggered");
}
}
#Configuration
#AnnotationDriven
public class AppConfiguration {
#Bean
public SimpleCommandBus commandBus() {
SimpleCommandBus simpleCommandBus = new SimpleCommandBus();
return simpleCommandBus;
}
#Bean
public Cluster normalCluster() {
SimpleCluster simpleCluster = new SimpleCluster("simpleCluster");
return simpleCluster;
}
#Bean
public ClusterSelector clusterSelector() {
Map<String, Cluster> clusterMap = new HashMap<>();
clusterMap.put("com.user.event.handler", normalCluster());
//clusterMap.put("exploringaxon.replay", replayCluster());
return new ClassNamePrefixClusterSelector(clusterMap);
}
#Bean
public EventBus clusteringEventBus() {
ClusteringEventBus clusteringEventBus = new ClusteringEventBus(clusterSelector(), terminal());
return clusteringEventBus;
}
#Bean
public EventBusTerminal terminal() {
return new EventBusTerminal() {
#Override
public void publish(EventMessage... events) {
normalCluster().publish(events);
}
#Override
public void onClusterCreated(Cluster cluster) {
}
};
}
#Bean
public DefaultCommandGateway commandGateway() {
return new DefaultCommandGateway(commandBus());
}
#Bean
public Repository<User> eventSourcingRepository() {
EventStore eventStore = new FileSystemEventStore(new SimpleEventFileResolver(new File("D://sevents.txt")));
EventSourcingRepository eventSourcingRepository = new EventSourcingRepository(User.class, eventStore);
eventSourcingRepository.setEventBus(clusteringEventBus());
AnnotationEventListenerAdapter.subscribe(new UserEventHandler(), clusteringEventBus());
return eventSourcingRepository;
}
}
As far as I can tell, the only thing missing is that you aren't adding the User Aggregate to a Repository. By adding it to the Repository, the User is persisted (either by storing the generated events, in the case of Event Sourcing, or its state otherwise) and all Events generated by the Command Handler (including the Aggregate) are published to the Event Bus.
Note that the Aggregate's #EventSourcingHandlers are invoked immediately, but any external #EventHandlers are only invoked after the command handler has been executed.