I've written some code using Spring Boot and Lombok. I've got two objects, user and loggedInUser both are based on User.class. I assign a different value to the each of the objects userEmail field. I got the following code. The user is what is in the request from the endpoint while the loggedInUser is the user performing the request.
This is the User.class:
#Component
#Data
public class User {
private String userEmail;
}
This is the class where I'm using User.class
private User user;
private User loggedInUser;
private SearchParameters searchParameters;
public UserController(
User user,
User loggedInUser,
SearchParameters searchParameters, {
this.user = user;
this.loggedInUser = loggedInUser;
this.searchParameters = searchParameters;
}
#GetMapping(value = "${apiVersion.v_1}" + "/users")
public ResponseEntity getUsers(
#RequestParam("userEmail") String userEmail,
#RequestParam("direction") String direction,
#RequestParam("limit") String limit) {
Jws<Claims> jwsClaims = (Jws<Claims>) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
loggedInUser.setUserEmail(jwsClaims.getBody().getSubject()); //From a JWT the logged in user's e-mail is picked up. In this case joe#example.com
log.info("loggedInUser.getUserEmail #1: " + loggedInUser.getUserEmail());
user.setUserEmail(userEmail); // From the request parameters the email is that will be used for the search is picked up. In this case smith#example.com
searchParameters.setDirection(direction); //Can be "u" or "d" that corresponds to "SELECT * FROM user_tbl WHERE < user.getEmail()" or "SELECT * FROM user_tbl WHERE > user.getEmail()" in the SQL that will run in another class
searchParameters.setLimit(limit); //An integer that will limit the number of returned results from the user_tbl
log.info("loggedInUser.getUserEmail #2: " + loggedInUser.getUserEmail());
//The search is done in another class that is taking user.getEmail() as an argument and returns a List of users
return (new ResponseEntity(users, headers, HttpStatus.OK));
}
It gives me the following output.
loggedInUser.getUserEmail #1: joe#example.com
loggedInUser.getUserEmail #2: smith#example.com
I would have thought that user and loggedInUser are two separate objects since they have different names and that the dependency injection is instantiate a new object each time but it seems like user is overwriting loggedInUser. What is it I'm missing here?
Related
I am trying create one application from where I can get the all the information related to user/Tracks/Album.
I have used below code for authentication using clientid and client secret id.
private static String clintId = "generated id";
private static String clientSecretId = "generated secret id";
private static final URI redirectUri = SpotifyHttpManager.makeUri("http://localhost:8080/api/get-user-code/");
public static final SpotifyApi spotifyApi = new SpotifyApi.Builder()
.setClientId(clintId)
.setClientSecret(clientSecretId)
.setRedirectUri(redirectUri)
.build();
#GetMapping("login")
#ResponseBody
public String spotifyLogin(){
AuthorizationCodeUriRequest authorizationCodeUriRequest = spotifyApi.authorizationCodeUri()
.scope("user-read-private user-read-email user-top-read user-library-read user-library-modify")
.show_dialog(true)
.build();
final URI uri = authorizationCodeUriRequest.execute();
return uri.toString();
}
#GetMapping(value="get-user-code")
public void getSpotifyUserCode(#RequestParam("code") String userCode, HttpServletResponse response) throws IOException {
AuthorizationCodeRequest authorizationCodeRequest = spotifyApi.authorizationCode(userCode)
.build();
try {
final AuthorizationCodeCredentials authorizationCodeCredentials = authorizationCodeRequest.execute();
// Set access and refresh token for further "spotifyApi" object usage
System.out.println("Access token::"+authorizationCodeCredentials.getAccessToken());
spotifyApi.setAccessToken(authorizationCodeCredentials.getAccessToken());
spotifyApi.setRefreshToken(authorizationCodeCredentials.getRefreshToken());
System.out.println("Expires in: " + authorizationCodeCredentials.getExpiresIn());
} catch (IOException | SpotifyWebApiException | org.apache.hc.core5.http.ParseException e){
System.out.println("Error: " + e.getMessage());
}
response.sendRedirect("http://localhost:3000/home");
}
Now take one example: Suppose I have created above client id and secret id from User A (From development dashboard) so for User A everything is working fine like I am able to get user profile, Track etc.
But suppose if I try to get same details for User B like user profile/tracks etc with User A generated client id and secret id then not able to get those information for User B its showing "forbidden".
So my question is that how to get those information for user B (with user A client id and secret details)?
I have managed to send a parameterised request based on the URL entered by the user, however I am not sure how to get the 2nd controller to properly receive the route for the URL field of the Project class object. How do I do this? I don't even know what to search.
//ProjectController
//Creating Project Object
Project project = new Project();
System.out.println("userID: " + userID);
//Getting values using webuser userid (Parameter that passed by model attribute)
WebUser webUser = webUserRepo.findUserById(Long.parseLong(userID));
//Setting values to project object to save
project.setUserID(webUser);
project.setDescription(description);
project.setTargets(targets);
project.setUrl(url);
// Getting date
Date date = new Date();
project.setPublishedOn(date);
//Save Project Object and get that saved Object for system.print
Project savedProject = projectRepo.save(project);
//Print saved Object
System.out.println("saved Project Object : "+ savedProject.toString());
//Set user id in model attribute for return page
model.addAttribute("userID", userID);
model.addAttribute("url", url);
return "/project-dashboard/" + savedProject.getUrl();
//Dashboard Controller:
#GetMapping("project/{url}")
public String serveProject(#PathVariable("url") String url, #RequestParam Long userID, Model model){
System.out.println("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO ADKLLDAL " + userID);
Project project = projectRepo.getById(userID);
System.out.println(project.toString());
model.addAttribute(project);
return "project";
}
Thanks for any help!
I have a simple web application with two entities:
User
State (with field: User ownerUser)
User can create and read own States. I created full text search (elasticsearch) for States but my search returns me all States, not only created by logged user. I tried something like this but it doesn't work:
public Page<StatusDTO> search(String query, Pageable pageable) {
log.debug("Request to search for a page of Statuses for query {}", query);
String login = SecurityUtils.getCurrentUserLogin();
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(queryStringQuery(query))
.withFilter(boolQuery().filter(termQuery("ownerUser : login", login)))
.build();
Page<Status> result = statusSearchRepository.search(searchQuery.getQuery(), pageable);
return result.map(statusMapper::toDto);
}
Can you try
.withFilter(boolQuery().filter(termQuery("ownerUser.login", login)))
The main data information and information in my API is linked to a project(Entity),
What is good approach for password flow: to manage specific permission linked to a project with spring security and OAuth2?
In this application you have 5 micro service:
UAA microservice : the authorization server
Catalog microservice
Order microservice
Invoice microservice
Customer microservice
Zoom permission :
Each user can have many project, and can have the permission for each project:
CAN_MANAGE_CATALOG
CAN_VIEW_CATALOG
CAN_MANAGE_ORDER
CAN_VIEW_ORDER
CAN_MANAGE_INVOICE
CAN_VIEW_INVOICE
...
I have many idea but i am not sure if i have the good approach :
USE CASE : I want to securise the endpoint :
http://catalog-service/{project_key}/catalogs
Only USER who have permission VIEW_CATALOG OR MANAGE_CATALOG for the project {project_key} can list all catalog present in the project
My first idea : USE ProjectAccessExpression with preauthorize
CatalogController.java
#Controller
public class CatalogController {
#PreAuthorize("#projectAccessExpression.hasPermission(#projectKey, 'manageCatalog', principal)" +
" or #projectAccessExpression.hasPermission(#projectKey, 'viewCatalog', principal)")
#RequestMapping(
value = "/{projectKey}/catalogs",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE
)
public #ResponseBody List<Catalog> findByProject(#PathVariable("projectKey") String projectKey) {
return catalogService.find();
}
}
ProjectAccessExpression.java
#Component
public class ProjectAccessExpression {
private RestTemplate restTemplate;
public boolean havePermission(String projectKey, String permission , String username) {
Boolean havePermission = restTemplate.getForObject(String.format("http://uaa-service/permission/check?project=%1&permission=%2&username=%3",
projectKey, permission, username
), Boolean.class);
return havePermission;
}
}
The inconvenient : is need to call UAA Service for each time
Second idea : USE USER_ROLE
With user_role
username | role
mylogin1 | SHOP1.CAN_MANAGE_CATALOG
mylogin1 | SHOP1.CAN_VIEW_CATALOG
mylogin1 | SHOP2.CAN_MANAGE_CATALOG
mylogin1 | SHOP2.CAN_VIEW_CATALOG
mylogin1 | SHOP2.CAN_MANAGE_ORDER
mylogin1 | SHOP2.CAN_VIEW_ORDER
...
SHOP1 is SHOP2 is projectKey
The inconvenient : i am not sure but if user change permission, i need to revoke all token associate
Third idea : add specific permission in authentication blob
I don't know how to do for storing...
And in controller a annotation :
#PreAuthorize("#ProjectAccessExpression.hasPermission(authentication, 'manageCatalog||viewCatalog', #projectKey)
The inconvenient : same inconvenient at second idea
It basically looks like your just trying to leverage roles with OAuth 2.0 for your project. Here is an excerpt of some spring documentation on OAuth 2.0
Mapping User Roles to Scopes: http://projects.spring.io/spring-security-oauth/docs/oauth2.html
It is sometimes useful to limit the scope of tokens not only by the scopes assigned to the client, but also according to the user's own permissions. If you use a DefaultOAuth2RequestFactory in your AuthorizationEndpoint you can set a flag checkUserScopes=true to restrict permitted scopes to only those that match the user's roles. You can also inject an OAuth2RequestFactory into the TokenEndpoint but that only works (i.e. with password grants) if you also install a TokenEndpointAuthenticationFilter - you just need to add that filter after the HTTP BasicAuthenticationFilter. Of course, you can also implement your own rules for mapping scopes to roles and install your own version of the OAuth2RequestFactory. The AuthorizationServerEndpointsConfigurer allows you to inject a custom OAuth2RequestFactory so you can use that feature to set up a factory if you use #EnableAuthorizationServer.
All this basically boils down to is that you can protect your endpoints with different scopes by mapping scopes to your own custom roles. This will allow you to get really fine grained with your security.
I found a pretty good walk-through you can use as a reference: (Obviously you'll have to configure the settings to your own use case)
https://raymondhlee.wordpress.com/2014/12/21/implementing-oauth2-with-spring-security/
This solution i use and work fine
** 1 - Load Business Logic Security when user sign **
This example find user with role persist in database, and add all role depending project. After the operation i have authentication token with
GrantedAuthority : ROLE_USER, ROLE_MANAGE_CATALOG:project1, ROLE_VIEW_PROFILE:project1, ROLE_MANAGE_PROJECT:project2, ...
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> user = userService.findByLogin(username);
if (!user.isPresent()) {
Object args[] = {username};
throw new UsernameNotFoundException(
messageSource.getMessage("user.notexist", args, "User {0} doesn't not exist", LocaleContextHolder.getLocale())
);
}
if (!user.get().isActivated()) {
//throw new UserNotActivatedException(String.format("User %s was not activated!", username));
Object args[] = {username};
throw new UserNotActivatedException(
messageSource.getMessage("user.notactivated", args, "User {0} was not activated", LocaleContextHolder.getLocale()));
}
//Here implement your proper logic
//Add busness logic security Roles
// eg ROLE_MANAGE_PROJECT:{project_key}, ROLE_MANAGE_CATALOG:{project_key}
List<Role> bRoles = projectService.getRolesForUser(username)
user.get().getRoles().addAll(
bRoles
);
UserRepositoryUserDetails userDetails = new UserRepositoryUserDetails(user.get());
return userDetails;
}
}
** 2 check security with preauthorize expression **
In this example only user who have this permission can do this operation:
ROLE_ADMIN OR
ROLE_MANAGE_PROJECT:{projectKey}
#PreAuthorize("#oauthUserAccess.hasPermission(authentication, '"+Constants.PP_MANAGE_PROJECT+"', #projectKey)")
#RequestMapping(
value="/projects/{projectKey}",
method = RequestMethod.PUT,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity updateProject(#PathVariable("projectKey") String projectKey,#Valid #RequestBody Project project)
The OauthUserAccess class :
#Component("oauthUserAccess")
public class OauthUserAccess {
/**
* Check if it is the administrator of the application IMASTER
* #param authentication
* #param projectKey
* #return
*/
public boolean hasAdminPermission(OAuth2Authentication authentication, String projectKey) {
if(authentication.getOAuth2Request().getAuthorities().contains("ROLE_ADMIN")) return true;
return false;
}
/**
*
* #param authentication
* #param permissionType
* #param projectKey
* #return
*/
public boolean hasPermission(OAuth2Authentication authentication, String permissionType, String projectKey) {
if (!ProjectPermissionType.exist(permissionType) ||
projectKey.isEmpty() ||
!projectKey.matches(Constants.PROJECT_REGEX))
return false;
if (authentication.isClientOnly()) {
//TODO check scope permission
if(authentication.getOAuth2Request().getScope().contains(permissionType+":"+projectKey)) return true;
}
if (hasAdminPermission(authentication, projectKey)) return true;
String projectPermission = "ROLE_" + permissionType + ":" + projectKey;
String projectPermissionManage = "ROLE_" + permissionType.replace("VIEW", "MANAGE") + ":" + projectKey;
String manageProject = "ROLE_" + Constants.PP_MANAGE_PROJECT + ":" + projectKey;
Predicate<GrantedAuthority> p = r -> r.getAuthority().equals(projectPermission) || r.getAuthority().equals(projectPermissionManage) || r.getAuthority().equals(manageProject);
if (authentication.getAuthorities().stream().anyMatch(p)) {
return true;
};
return false;
}
}
3 - Advantage / disadvantage
Advantage
the business logic permission is loaded only when user login to the application and not every times so it is powerfull solution for microservice architecture.
Disadvantage
Need to update authentication token or revoke the token when permission change else
when you update the permission for user, the user required to logout and login. But you have same issue without this security logic, for example when user is disabled or enable.
My solution i use for example in a controller :
newAuthorities = projectService.getRolesForUser(username);
UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), newAuthorities);
OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByUserName(principal.getName());
OAuth2Authentication auth2 = new OAuth2Authentication(authentication.getOAuth2Request(), newAuth);
accessTokens.forEach(token -> {
if (!token.isExpired()) {
tokenStore.storeAccessToken(token, auth2);
}
});
I'm currently working on a (relatively) basic web application that functions as a time clock for a fictional employer. Currently, the application implements HTML, JavaScript, Java, MySQL, the Spring Framework and xml. I have a log in page, and it takes the user input for the Username and Password and stores the information. The problem is, I can't seem to figure out how to take that information and compare it with the MYSQL database to successfully log in. I can post examples of code, if necessary and thanks in advance for the assistance.
This is a piece of the LoginController:
#RequestMapping(value = "/jsp/login")
public ModelAndView existUser(HttpServletRequest request) {
return new ModelAndView("UserPrint.jsp", hashmap.makeHashMap());
}
#RequestMapping(value = "userLogin")
public ModelAndView loginUser(HttpServletRequest request,
HttpSession session) {
String strLoginJsp = "login.jsp";
String strSessionErrorAttribute = "errors";
User sessionUser = getUserFromSession(session);
if (request.getHeader("referer") == null
|| request.getHeader("referer").contains("AddNewUser.html")) {
session.setAttribute(SESSION_USER_ATTRIBUTE, new User());
session.setAttribute(strSessionErrorAttribute, "");
return new ModelAndView(strLoginJsp, hashmap.makeHashMap());
}
User requestUser = new User();
String requestedUserName = request.getParameter("userName");
requestUser.setUserName(requestedUserName);
String requestedPassword = request.getParameter("password");
requestUser.setPassword(requestedPassword);
session.setAttribute(strSessionErrorAttribute, "");
// Check if username and password is in DB
User loginUser = userDao.login(requestUser);
if (loginUser == null) {
// Either the username doesn't exist, or the password was bad.
if (userDao.userNameExist(requestedUserName)) {
// user entered bad password
if (sessionUser.getUserName() != null
&& sessionUser.getUserName().equals(
requestUser.getUserName())) {
} else {
sessionUser.setUserName(requestUser.getUserName());
}
}
} else {// username does not exist in db
session.setAttribute(strSessionErrorAttribute,
"Please register account.");
Here's a piece of the UserDao:
#SuppressWarnings("unchecked")
#Transactional
public List<User> getAllUsers() {
Query query = em.createNamedQuery("fetchAllUsers");
return query.getResultList();
}
public User login(User user) {
Query query = em.createNamedQuery("userLogin");
query.setParameter("userName", user.getUserName());
query.setParameter("password", user.getPassword());
List<User> currentUsers = query.getResultList();
if (currentUsers.size() > 0) {
return (User) currentUsers.get(0);
}
return null;
}
#SuppressWarnings("unchecked")
#Transactional
public Boolean userNameExist(String userName) {
Query query = em.createNamedQuery("getUserWithUsername");
query.setParameter("userName", userName);
List<User> existUser = query.getResultList();
if (existUser.size() > 0) {
return true;
}
return false;
}
To expand on dm03514 's answer - yes you should "sanitize" user input by using prepared statement objects in Java JDBC.
If you're using Spring then you should use spring-security. They have pretty much done this for you already and you just have to configure it.
Normally you would take your login and password and search for a user
String queryStr = "SELECT * FROM users WHERE login='"+sanitize(userLogin)+"' AND password='"+sanitize(userPass)"'";
This is not actual code just psuedo code. I do not know how java or spring handles sanitation but it is of utmost importance that you dont' trust anything input by the user when you are building a query string.
The idea is if this results in 1 user the user is valid, if it returns none the user is not valid!
Using paramaterized queries could be the best approach
Yes, please, post some code.
What exactly is the issue?
On the most basic level you should be able to:
pass the username and password as parameters in the HttpServletRequest object
Retrieve said parameters by "name" from the HSR object in the Controller.
Pass them to the SQL query for comparison against the database field.
Which of the above steps failed?