I'm using a Apache CXF with WebClient and I want to send my credentials to the server
WebClient client = WebClient.create("http://localhost:8084", "username", "password", null)
The problem is that I can't get the password value.
The following method is not working
SecurityContextHolder.getContext().getAuthentication().getCredentials()
Lets say that you been login and you need to get company name that you been retrieve from database. First you will need to object that implement UserDetails that will save in UserDetailsService and add any variable that you need (company, etc).
public class CustomUserDetails implements UserDetails{
private String password;
private String username;
private String companyName;
}
than at typecast with your custom Userdetails implementation.
CustomUserDetails customDetails (CustomUserDetails)SecurityContextHolder.getContext().getAuthentication().getCredentials();
getCredentials will return Object of what you saved to the credential. Whatever it type you can always return it to the class you been save.
Related
I am developing a Authentication API (Login and Register), what I want exactly is after the user authenticate, I want to return the user profile.
currently I have an AuthApi class that called an AuthService this service called the appropriate use case.
public class AuthService {
private final Login login;
private final Register register;
public AuthUserOutputRequest loginUser(LoginRequestModel model) {
log.info("loginUser Service is running");
return login.execute(model);
}
public AuthUserOutputRequest registerUser(RegisterRequestModel model) {
log.info("registerUser Service is running");
return register.execute(model);
}
}
after that I do the login or register logic.
all this happen inside my Auth domain so the user model inside this domain dose only care about email, password and user role
public class AuthUser {
/// rules
private static final int passwordLength = 5;
private static final String emailPattern = "^(?=.{1,64}#)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*#"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
private String username;
private String password;
private AuthUser(String username, String password) {
this.username = username;
this.password = password;
}
public static AuthUser createUser(String username, String password) {
if(password.length() < passwordLength) {
throw ValidationException.create("Password must be at least 5 characters");
} else if(!Pattern.compile(emailPattern).matcher(emailPattern).matches()) {
throw ValidationException.create("Invalid Email");
} else {
return new AuthUser(username, password);
}
}
}
now I am lost.
solutions in my mind:
1- add another use case for getting the user profile and call it from the service layer, but this use case will be inside another domain (maybe move it to shared folder).
2- should the controller have 2 services and call the authenticate first then the get profile (probably wrong because controller now has logic).
3- make http client and call the getProfile API from the controller after authentication happened.
Notes my understanding of clean architecture:
service layer in my case is just a wrapper for calling the use cases like maybe one API need to call multiple use cases, I do this kind of logic inside my service layer.
use cases operations on my domain, I do the business logic in this layer.
Domain Object holds domain data, and business rules.
please correct my info, if I am wrong.
I have problem with testing rest controller endpoint.
I'm trying to POST entity to endpoint that is annotated with #Valid on #RequestBody field. Entity fields are annotated with validation rules and jackson annotations.
I'm trying to test that POST endpoint with unit tests.
Entity and field with annotations
public class User {
//other fields
#Column(name = "PASSWORD", nullable = false)
#NotEmpty(message = "Users password must be filled.")
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String userPassword;
}
Controller
#RestController
#RequestMapping(value = "/api/users", produces = "application/hal+json")
public class UserController {
#PostMapping("")
public ResponseEntity<User> addNewUser(#Valid #RequestBody User newUser) {
User createdUser = userService.saveUserInDatabase(newUser);
return new ResponseEntity<>(createdUser, HttpStatus.OK);
}
}
And unit test
#RunWith(MockitoJUnitRunner.class)
public class UserControllerTest {
//other tests
#Test
public void shouldBeAbleToAddNewUser() throws Exception {
User newUser = new User();
newUser.setUserName("userName");
newUser.setUserEmail("userName#domain.com");
newUser.setUserPassword("secret1");
newUser.setEnable(true);
User createdUser = new User(1L, "userName", "userName#domain.com", "secret1", true);
when(userService.saveUserInDatabase(any(User.class))).thenReturn(createdUser);
mockMvc.perform(post("/api/users")
.contentType(MediaTypes.HAL_JSON_UTF8)
.content(jsonUser.write(newUser).getJson()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.userName", Matchers.equalToIgnoringCase(createdUser.getUserName())))
.andExpect(jsonPath("$.userId", Matchers.is(createdUser.getUserId().intValue())))
.andExpect(jsonPath("$.userPassword").doesNotExist());
}
}
What is important. When entity is fetched (GET) with swagger, field "userPassword" is not serialized. It works as expected thanks to #JsonProperty annotation. When entity is POSTed with swagger is also works correctly, password is saved in database and response doesn't contain "userPassword" field. Also, if any required (annotated with #NotEmpty) field is missing, exception is thrown.
Tests that are testing fetching data, works correctly, 'userPassword' is not visible response in json
andExpect(jsonPath("$.userPassword").doesNotExist())
But when attached test is executed, it fails with wrong status (400 instead 200) and information "Users password must be filled."
Password is filled but it looks like it is not pushed to request body. I did some debug and notice that when #JsonProperty is added to "userPassword" field, that field is set to "null".
//edit
jsonUser used to post new User data in JSON format is defined this way
#RunWith(MockitoJUnitRunner.class)
public class UserControllerTest {
//other variables and methods
private JacksonTester<User> jsonUser;
#Before
public void setup() {
JacksonTester.initFields(this, new ObjectMapper());
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
}
//edit
I've found solution for my problem.
jsonUser.write to work must first read data from newUser (so obviously) and include json annotation. As getter is blocked by jsonproperty it is omitted during read. That is why test case din't work but POST through swagger works correctly.
To solve it, I've prepared String that contain data in json format
String userDetails = "{\"userName\":\"userName\",\"userEmail\":\"userName#domain.com\",\"userPassword\":\"secret1\",\"enable\":true}";
mockMvc.perform(post("/api/users")
.contentType(MediaTypes.HAL_JSON_UTF8)
.content(userDetails))
.andExpect(status().isOk());
You set the content of the post with
jsonUser.write(newUser).getJson()
jsonUser is a JacksonTester and is given a Jackson ObjectMapper to perform serialization / deserialization. As you are using the write method to serialize your user object it will respect the
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
The confusion maybe the use of 'write'. The method on the JacksonTester is write() which means serialize (write the object to json) where as the JsonProperty.Access.WRITE_ONLY means only use this field during deserialize (write from the json to the object)
And the Json you get will not include any password details. i.e. you get something like:
{"userName":"userName","userEmail":"userName#domain.com","enable":true}
You use this as the content for your test POST request. When Jackson comes to deserialize your json from the content of the POST request it won't find a password. This is why you see what you see.
I understand why you're using the WRITE_ONLY option on your class, it makes sense to prevent the password being written out when it is serialized. However, that means you can't use that class to create the json that you want to send up to the server by simply serializing it.
One approach would be to subclass your User with a TestUser which just had a password field. Then use TestUser in your JacksonTester.
public class TestUser extends User {
private String userPassword;
}
Then in your test class you'd replace User with TestUser
private JacksonTester<TestUser> jsonUser;
and
TestUser newUser = new TestUser();
newUser.setUserName("userName");
newUser.setUserEmail("userName#domain.com");
newUser.setUserPassword("secret1");
newUser.setEnable(true);
My ModelClass
public class UserPojo{
private String userEmailId;
private String userPassword;
private String userContactNumber;
//setters and getters
}
I want to send UserPojo class object as json but In somecases I want to send with userpassword and somecases without userpassword is it possible?
In below method I want to send userPojo object without userpassword.
My Spring version is 4.3.1.RELEASE. I have Jackson libraries
#RestController
#RequestMapping("/user")
public class UserController{
#GetMapping(value="/{userId}")
public UserPojo getUser(#PathVariable("userId") String userId){
//code goes here
//finally returning UserPojo Object
return userPojo;
}
}
In below method I want to send userPojo object with password
#RestController
#RequestMapping("/admin")
public class AdminController{
#GetMapping(value="/{userId}")
public UserPojo getUser(#PathVariable("userId") String userId){
//code goes here
//finally returning UserPojo Object
return userPojo;
}
}
I hope you got my point
For your requirement use #JsonView, if you want to ignore totally some field then #JsonIgnore.
Yes, this is achieved by using #JsonView annotation.
Here is a nice tutorial how to use this feature
You can add this annotation at the field level and that attribute will be excluded from the JSON response.
#JsonProperty(access = Access.WRITE_ONLY)
private float height;
Design pattern help:
I have a dropwizard application that implements JWT for authentication. I have this working in the most basic form.
I have a User class which has all sorts of details about a user (email, name, settings etc)
However I've been advised that when I authenticate the user I should be returning a Principal rather than the actual user. Here is my method that does this:
#Override
public Optional<Principal> authenticate(JsonWebToken token) throws AuthenticationException {
final User user = userCollection.findOneById(token.claim().subject());
Principal principal = new Principal() {
#Override
public String getName() {
return user.getUsername();
}
};
return Optional.of(principal);
}
I have the users stored in a mongoDB collection, so using the ID from the token I can retrieve that specific user and assign that users name to a new principal object and return that principal.
This means in my endpoints that require authentication, I can use properties from that Principal for example:
#POST
#Path("project-create")
public Response setProject(#Auth Principal principal, #Valid Project project) {
[...]
project.setAuthorName(principal.getName());
[...]
}
This is all working, but what if I wanted to access other properties of the user which don't exist on the Principal?
Should I be using the name value of the principal to lookup the user in the database and retrieve that user each time I want to use one of the users properties?
Is there a massive flaw in what I've done? I don't have much experience on this front and find it hard to find real world examples around this.
Would appreciate some guidance around what sort of pattern/workflow I should be following.
The Authenticator generic type is Athenticator<C, P extends Principal>, so you can make your User implement Principal and make your Authenticator something like
public class JWTAuthenicator extends Authenticator<JsonWebToken, User> {
#Override
public Optional<User> authenticate(JsonWebToken token) {}
}
Then build the JWTAuthFilter with the User generic type argument
new JWTAuthFilter.Builder<User>()
.setAuthenticator(new JWTAuthenticator())
.setEverythingElse(...)
.buildAuthFilter();
I am using spring security in my application and ran into a problem. My application has an Update Profile page. I have added preAuthorized() with request mapping as
#PreAuthorize("isAuthenticated()")
#RequestMapping (value="/user/{uid}/profile/update", method = GET)
public String updateProfileView(#ModelAttribute("form") UserProfileForm form, #PathVariable ("uid") Integer userId, Model model){
It works fine, and unauthenticated user can not access this page.
But the issue is that every Authenticated User can access this page.
For example : User A logged in into application, he/she will be able to update every one's profile.
My CustomUserDetailService class is
#Service
#Transactional
public class CustomUserDetailsService implements UserDetailsService {
#Resource
UserService userService;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
com.analyst.future.domain.User user = userService.getUser(email);
SimpleGrantedAuthority auth = new SimpleGrantedAuthority("ROLE_USER");
Collection<SimpleGrantedAuthority> authorities = new HashSet<SimpleGrantedAuthority>();
authorities.add(auth);
User userDeatails = new User(user.getEmail(), user.getPassword(), authorities);
return userDeatails;
}
}
I don't think i can restrict it with roles as every authenticated user will have same roles.
Is there any way i can restrict Authenticated user to access only self update profile page.
I am no Spring Security expert, but try reading up on using Expression-Based Access - Link here
There is one tiny little line that matches what you want -
For example, if you wanted a particular method to only allow access to a user whose username matched that of the contact, you could write
#PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);
I think in your case it would be something like
#PreAuthorize("email == authentication.email")
This is method level though, so maybe not what you are looking for? Good news is that there is a way to use the logged in user and match it against the request user. :)
Since all previous answers talk about matching username (which is included in the principal object) but you need to match the userID, this needs a little more work. We first need to return a custom User object that extends UserDetails. Here is how you can do that:
Step 1: Make your 'User' model implement UserDetails
#Entity
public class UserAccount implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty("id")
private int userId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String email;
private String password;
// other fields and methods
}
You will also have to override some methods from UserDetails, which is easy to figure out.
Step 2: Make you User Service implement UserDetailsService
#Component
public class UserAccountService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) {
// todo
}
}
Step 3: Return YOUR user model from the loadUserByUsername method
#Override
public UserDetails loadUserByUsername(String username) {
Optional<UserAccount> userAccount = userAccountDao.findByEmail(username);
if (!userAccount.isPresent()) {
throw new MyException("User account not found for the given Username.", HttpStatus.NOT_FOUND);
}
return userAccount.get();
}
Step 4: Add the #PreAuthorize expression to your resource
#PutMapping(value = "/{userId}")
#PreAuthorize("#userId == authentication.principal.userId or hasAuthority('ADMIN')")
public UserAccount updateUserAccount(#PathVariable("userId") int userId,
#RequestBody UserAccount userAccount) {
return userAccountService.updateUserAccount(userId, userAccount);
}
Important things to notice above are:
We can check if the userId is equal above, only because our custom UserAccount model has a userId. If we had returned a raw UserDetail (like org.springframework.security.core.userdetails.User) object instead, it will not have any 'id' property to match with, so above will fail.
Above #PreAuthorize annotation checks for 2 conditions. It allows the request if the user is the owner of the Account or if the user has ADMIN authority.