I want to create an entity that has a Many to One relationship with JHipster's User entity.
I've used JDL-Studio to create the following entity and relationship to User which is imported into my Microservice as a .jh file using jhipster import-jdl:
entity Profile {
name String required
bio String
location String
photo ImageBlob
}
relationship ManyToOne {
Profile{user} to User
}
angularSuffix * with mySuffix
dto * with mapstruct
Upon compiling my Micro Service I get the following errors:
Profile.java:44: error: cannot find symbol private User user; symbol: class User location: class Profile
ProfileMapper.java:12: error: cannot find symbol
#Mapper(componentModel = "spring", uses = {UserMapper.class, }) symbol: class UserMapper
ProfileMapper.java:12: error: Couldn't retrieve #Mapper annotation
#Mapper(componentModel = "spring", uses = {UserMapper.class, })
Lines 43-44 of Profile.java are:
#ManyToOne
private User user;
and ProfileMapper.java is as follows:
package com.moogrisoft.openseas.service.mapper;
import com.moogrisoft.openseas.domain.*;
import com.moogrisoft.openseas.service.dto.ProfileDTO;
import org.mapstruct.*;
import java.util.List;
/**
* Mapper for the entity Profile and its DTO ProfileDTO.
*/
#Mapper(componentModel = "spring", uses = {UserMapper.class, })
public interface ProfileMapper {
#Mapping(source = "user.id", target = "userId")
ProfileDTO profileToProfileDTO(Profile profile);
List<ProfileDTO> profilesToProfileDTOs(List<Profile> profiles);
#Mapping(source = "userId", target = "user")
Profile profileDTOToProfile(ProfileDTO profileDTO);
List<Profile> profileDTOsToProfiles(List<ProfileDTO> profileDTOs);
/**
* generating the fromId for all mappers if the databaseType is sql, as the class has relationship to it might need it, instead of
* creating a new attribute to know if the entity has any relationship from some other entity
*
* #param id id of the entity
* #return the entity instance
*/
default Profile profileFromId(Long id) {
if (id == null) {
return null;
}
Profile profile = new Profile();
profile.setId(id);
return profile;
}
}
There is no UserMapper class in my project as non was generated for me.
If I create a Monolithic application then creating the entity relationship works fine, it's only a problem with a Microservice because of the missing User class and related classes.
In a JHipster microservice architecture, the User entity is located on the gateway or UAA service.
Profile Entity
In the UAA, you can create an entity with a relationship to the user. Adding additional information such as a profile to the entity is a good use case for this. Doing the same thing in a gateway is also possible. An example UAA that creates an entity with a relationship to the user can be seen https://github.com/xetys/microxchng-workshop/tree/master/uaa
Blog Entity
Using the entity sub-generator works a little bit differently in a microservices architecture, as the front-end and the back-end codes are not located in the same application.
https://jhipster.github.io/microservices-architecture/#generating_entities
For this type of entity, I recommend storing the user's ID along with the entity. You will need to pass the user ID from the client as the ID is not easily accessible from a microservice. The reason we use the user ID instead of login is that the login can be modified through the User Management pages, causing the relationships to be fail (from the old login value).
The other option is to use the userLogin, which is accessible in a microservice through SecurityUtils.java. As long as you handle that the login can change, this is another option.
Related
I am trying to get the new AutoPopulate attribute to work but I am having some difficulty understanding the new AutoQuery functionality.
To test it out I am aiming to replace this service that is a standard AutoQuery endpoint but it also filters by the logged in users ID. I want to replace it so it works completely with just the model definition.
public class DevExtremeService : ServiceBase
{
public IAutoQueryDb AutoQuery { get; set; }
public QueryResponse<DeWatchedUrlResponse> Any(WatchedUrlDevExRequest request)
{
var q = AutoQuery.CreateDevXQuery(request, Request.GetRequestParams(), Request);
q.Where(x => x.UserAuthCustomId == GetUserId());
var response = AutoQuery.Execute(request, q, base.Request);
return response;
}
}
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
}
So I deleted the service and updated model to:
[ValidateIsAuthenticated]
[AutoPopulate(nameof(WatchedUrlDevExRequest.UserAuthCustomId), Eval = "userAuthId")]
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
public long UserAuthCustomId { get; set; }
}
My understanding from reading the release notes is that userAuthId is a variable declared in the AutoQuery #script context that is added by default.
I have tried a few different variations and I cannot get the property to populate. The docs seem focused on audit history and multitenancy but really I am just looking for a quick way to make endpoints.
I have 2 main questions:
Why is the auto populate not working on this property?
Where can I see the default #script definition so I can see how things like userAuthId are defined and better get an understanding how to add my own?
edit
I re-read docs and I gues this only works when writing data to db. I really like the concept of being able to apply #script to a request model via attribute. Is that possible?
AutoQuery CRUD's [AutoPopulate] attribute initially only populated AutoQuery CRUD's Data Model when performing CRUD operations, e.g. Inserting, Updating or Deleting entities.
For ensuring a query only returns a users records, it's recommended to use an AutoFilter instead, which behaves as expected ensuring the query is always applied to the Data Model, e.g:
[ValidateIsAuthenticated]
[Route("/de/watched-urls")]
[AutoFilter(QueryTerm.Ensure, nameof(WatchedUrlRecord.UserAuthCustomId),
Eval = "userAuthId")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
}
However as I can see it's a useful feature I've also just added support for [AutoPopulate] & [AutoMap] attributes on Query DTOs in this commit where your AutoQuery DTO would work as expected where it populates the Request DTO property:
[ValidateIsAuthenticated]
[AutoPopulate(nameof(WatchedUrlDevExRequest.UserAuthCustomId), Eval = "userAuthId")]
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
public long UserAuthCustomId { get; set; }
}
This change is available from v5.10.3 that's now available on MyGet.
An alternative approach to populate AutoQuery's Request DTO you could have a custom AutoQuery implementation like you have, an Extensible Query Filter or custom base class or I'd personally go with a Global Request Filter that updates all Request DTOs with a shared interface, e.g:
GlobalRequestFilters.Add((req, res, dto) => {
if (dto is IHasUserAuthCustomId authDto)
{
var session = req.GetSession();
if (session.IsAuthenticated)
authDto.UserAuthCustomId = session.UserAuthId;
}
});
Or you could wrap this logic in a Request Filter Attribute and apply the behavior to Request DTOs that way.
Note: userAuthId is a ServiceStack #Script method that returns the currently authenticated User Id.
I have two Bounded Context (studentenrollment, courses).
Studentenrollment has all the student with his course ids and his homework.
Courses have the admin part that content all the information related with the course.
When a student want to get information of the course, it hits an endpoint( /courses/ID) sending the jwt token. In the course context I get the student ID, course ID and create query that it's dispatched in the bus. In the query handler before getting the information of the course from the course ID, I want to validate if the student ID exist and this student has this course. For that I have to call the another context bounded studentenrollment. So, I was looking for how to handle that on internet and I found this:
https://medium.com/#martinezdelariva/authentication-and-authorization-in-ddd-671f7a5596ac
class findByCourseIdAndStudentIdQueryHandler()
{
public function handle($findByCourseIdAndStudentIdQuery)
{
$courseId = $findByCourseIdAndStudentIdQuery->courseId();
$studentId = $findByCourseIdAndStudentIdQuery->studentId();
$student = $this->collaboratorService->studentFrom(
$courseId,
$studentId
);
$this->courseRepository->findByCourseId($courseId);
}
}
class collaboratorService()
{
public function studentFrom($courseId, $studentId)
{
$student = $this->studentEnrollmentClient->getStudentFrom($courseId, $studentId);
if (!$student) {
throw new InvalidStudentException();
}
return $student;
}
}
What do you think?
UPDATED
namespace App\Context\Course\Module\Course\UI\Controller;
class GetCourseController extends Controller
{
public function getAction($request) {
$this->ask(new FindByCourseIdQueryHandler($request->get('course_id'));
}
}
namespace App\Context\Course\Module\Course\Infrastracture\Query;
class AuthorizedQueryDispatcher extends QueryDispatcher
{
//In this case $query would be FindByCourseIdQueryHandler
public function handle($query)
{
$authUser = $this->oauthService->getAuthUser();
//it can be student or teacher
$role = $authUser->getRole();
$userId = $authUser->getUserId();
//it will return FindByCourseIdAndStudentIdAuthorizedQueryHandler
$authorizedQuery = $this->inflector->getAuthorizedQueryName->from($userId, $role, $query);
$this->dispatch($authorizedQuery);
$this->queryDispatch->dispatch($query);
}
}
namespace App\Context\Course\Module\Course\Application\Query;
class FindByCourseIdAndStudentIdAuthorizedQueryHandler
{
public function handle($findByCourseIdAndStudentIdQuery)
{
$student = $this->studentEnrollmentClient->getStudentFrom($findByCourseIdAndStudentIdQuery->courseId, $findByCourseIdAndStudentIdQuery->studentId);
if (!$student) {
throw new InvalidStudentException();
}
}
}
namespace App\Context\Course\Module\Course\Application\Query;
class findByCourseIdAndStudentIdQueryHandler()
{
public function handle($findByCourseIdQueryHandler)
{
$courseId = $findByCourseIdQueryHandler->courseId();
$this->courseRepository->findByCourseId($courseId);
}
}
TLDR; Authorization should be clearly separated from the Domain layer, for example in a different package/namespace/module. Also, the dependency from the Domain to the Authorization should be inverted, the Domain should not depend/know about the authorization/
One way to implement it is to create an Authorization service, for example FindByCourseIdAndStudentIdQueryAuthorizer (let's name it Authorizer). This service may cross Bounded context (BC) boundaries, i.e. it could depend on remote domain services from remote BCs. Ideally, the remote data should be already available when the Authorizer does the checking. In this way the system is more resilient in case remote Bounded context services are not available. You can do this by listening to remote events or by background tasks.
Ideally, the domain layer (from any BC) should not know about the Authorizers.
One way to do this is to decorate the QueryDispatcher (or what you have) in the Composition root of the application with an AuthorizedQueryDispatcher. This AuthorizedQueryDispatcher, when it receives a query, it first search an Authorizer and then calls it. If the authorization fails then the query is rejected. If the authorization succedds or there is not authorizer then the query is sent to the real/decorated QueryDispatcher.
If can't do this (i.e. you don't have a QueryDispatcher) then you can try to decorate every query handler (by hand?). For example, you could have a FindByCourseIdAndStudentIdAuthorizedQueryHandler that has the same interface as the FindByCourseIdAndStudentIdQueryHandler. You could replace them in the composition root of the application (DIC).
Im working on rebuilding a clients software and they want to keep their database as unmodified as possible.
I got a table where they collect users and orders for different companies, no biggie there but the twist is they do it for multiple entities.
for example the table looks like this:
ID
UserID
Index
CompanyID
Type
lets say they got entities like Project and Workflow, then the Type column would be 'P' for projects and 'W' for workflows. So on a ID is the ID of a Project or Workflow Identity. UserID is always a foreign key to a User entity and Index is the order that the user is used when this Project/Workflow is used. And CompanyID is what company owns project or workflow entity.
I have tried to search google for this but i came up with nothing.
What i want is on a Template entity map two collections say StandardProjectUsers and StandardWorkflowUsers and they should collect them from correct entities with a user and index for current company.
Is this at all possible with fluent nhibernate ?
A nice article on how to do it: http://www.philliphaydon.com/2011/08/fluent-nhibernate-table-inheritance-discriminators/
You are looking at a table-per-hierarchy strategy.
In a nutshell you use:
public class BaseClassMap : ClassMap<BaseClass>
{
public BaseClassMap()
{
DiscriminateSubClassesOnColumn("Type");
...
}
}
public class WorkflowMap : SubclassMap<Workflow>
{
public WorkflowMap()
{
DiscriminatorValue("W");
...
}
}
public class ProjectMap : SubclassMap<Project>
{
public ProjectMap()
{
DiscriminatorValue("P");
...
}
}
I am working on Integrating spring security with openId for my grails Application using springsecurity core and springsecurity openid plugins. I have integrated it, and it works well but I need to access the email for the logged in person. How can I get that, all that I am able to access is a token which is used for identifying the person.
Thanks to Ian Roberts.
He gives me this reply,Which exactly solves my problem.
His reply was:
As it happens I implemented exactly this in one of my applications
yesterday :-) Unfortunately it's not an open-source app so I can't just
point you at my code but I can explain what I did.
The spring-security-openid plugin supports the "attribute exchange"
mechanism of OpenID, although the support is not documented much (if at
all). How well it works depends on the provider at the far end but this
at least worked for me using Google and Yahoo.
In order to request the email address from the provider you need to add
the following to Config.groovy:
grails.plugins.springsecurity.openid.registration.requiredAttributes.email
= "http://axschema.org/contact/email"
Now to wire that into your user registration process you need an email
field in your S2 user domain class, and you need to edit the generated
OpenIdController.groovy in a few places.
add an email property to the OpenIdRegisterCommand
in the createAccount action there's a line
"if(!createNewAccount(...))" which passes the username, password and
openid as parameters. Change this along with the method definition to
pass the whole command object instead of just these two fields.
in createNewAccount pass the email value forward from the command
object to the User domain object constructor.
And finally add an input field for email to your
grails-app/views/openId/createAccount.gsp.
You can do the same with other attributes such as full name.
grails.plugins.springsecurity.openid.registration.requiredAttributes.fullname
= "http://axschema.org/namePerson"
The important thing to wire it together is that the thing after the last
dot following requiredAttributes (fullname in this example) must match
the name of the property on the OpenIdRegisterCommand.
Regards
Charu Jain
I've never used the springsecurity openid plugin, but when using springsecurity core you can expose additional information about the current user by implmenting a custom UserDetails. In my app, I added this implementation, so that I can show the name property of logged-in users. You'll need to change this slightly, so that the email address is exposed instead
/**
* Custom implementation of UserDetails that exposes the user's name
* http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/11%20Custom%20UserDetailsService.html
*/
class CustomUserDetails extends GrailsUser {
// additional property
final String name
CustomUserDetails(String username,
String password,
boolean enabled,
boolean accountNonExpired,
boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
long id,
String displayName) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id)
this.name = displayName
}
}
You then need to create a custom implementation of UserDetailsService which returns instances of the class above
class UserDetailsService implements GrailsUserDetailsService {
/**
* Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
* we give a user with no granted roles this one which gets past that restriction but
* doesn't grant anything.
*/
static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]
UserDetails loadUserByUsername(String username, boolean loadRoles) {
return loadUserByUsername(username)
}
UserDetails loadUserByUsername(String username) {
User.withTransaction { status ->
User user = User.findByUsername(username)
if (!user) {
throw new UsernameNotFoundException('User not found', username)
}
def authorities = user.authorities.collect {new GrantedAuthorityImpl(it.authority)}
return new CustomUserDetails(
user.username,
user.password,
user.enabled,
!user.accountExpired,
!user.passwordExpired,
!user.accountLocked,
authorities ?: NO_ROLES,
user.id,
user.name)
}
}
}
You need to register an instance of this class as a Spring bean named userDetailsService. I did this by adding the following to Resources.groovy
userDetailsService(UserDetailsService)
I have a couple of tables and have defined relationships for them.
{Table Department} {Table Unit} {Table Branch}
A Department can have more than one branch, a branch can only belong to one department. I need to be able to get the department name, departmentid, branchname
Branch has an instance of departmentid in it.
How do I pull this in one ORM call?
class Model_Admin_Departments extends ORM
{
protected $_has_many = array('branches' => array ());
class Model_Admin_Branches extends ORM
{
protected $_belongs_to = array('departments ' => array());
I have also created the foreign key constraints on the db side with action cascade on delete. Could this cause problems or that is fine?
Assuming you have the right relationships declared you should be able to use the with(...) method on your ORM object.