Change Password API Spring Boot - java

I'm writing a program that changes a member's password, I fetched the user by id from the database when I test the endpoint on postman it returns 200 OK, but fails to update the password in the database to the new password, What is the right logic to use for this task? my code is below.
Member
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name ="member",
indexes = {
#Index(
columnList = "email_address",
name = "email_address_idx",
unique = true
),
},
uniqueConstraints = {
#UniqueConstraint(
columnNames = {"email_address", "phone_number"},
name = "email_address_phone_number_uq"
)
}
)
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "nationality_id")
private Country nationality;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "country_of_residence_id")
private Country countryOfResidence;
#Temporal(TemporalType.DATE)
#Column(name ="date_of_birth")
private Date dateOfBirth = new Date();
#Column(name ="current_job_title")
private String currentJobTitle;
#Column(name = "email_address", nullable = false)
private String emailAddress;
#Column(name = "username")
private String username;
#Column(name ="phone_number")
private String phoneNumber;
#Column(name ="city")
private String city;
#Column(name ="state")
private String state;
#Column(name ="password", nullable = false)
private String password;
}
PasswordDto
#Data
public class ChangePasswordDto {
private String password;
private String oldPassword;
private String newPassword;
private String reNewPassword;
PasswordService
#Slf4j
#Service
public class ChangePasswordServiceImpl implements ChangePasswordService {
#Autowired
private ModelMapper modelMapper;
#Autowired
private PasswordEncoder passwordEncoder;
private final PasswordJpaRepository jpaRepository;
public ChangePasswordServiceImpl(PasswordJpaRepository jpaRepository) {
this.jpaRepository = jpaRepository;
}
#Override
#Transactional
public Member changePassword(Long id, ChangePasswordDto password) {
final Member member = jpaRepository.findById(id);
Member getPassword = new Member();
getPassword = modelMapper.map(id, Member.class);
Member updatedPassword = new Member();
if (member.getPassword().equals(checkIfValidOldPassword(member, password.getOldPassword()))){
if (password.getNewPassword().equals(password.getReNewPassword())) {
updatedPassword = changPassword(member, password.getNewPassword());
}
}else{
return null;
}
return updatedPassword;
}
#Override
#Transactional
public boolean checkIfValidOldPassword(Member member, String oldPassword) {
return matches(oldPassword, member.getPassword());
}
#Override
#Transactional
public Member changPassword(Member member, String password) {
member.setPassword(password);
jpaRepository.save(member);
return member;
}
}
PasswordController
#RestController
#RequestMapping(
value = "password",
produces = { MediaType.APPLICATION_JSON_VALUE }
)
public class ChangePasswordController {
private ChangePasswordService service;
public ChangePasswordController(ChangePasswordService passwordService) {
this.service = passwordService;
}
#PostMapping("/change-password/{id}")
public Member changePassword(#Validated #RequestBody ChangePasswordDto password, #PathVariable(name = "id") Long id){
return service.changePassword(id, password);
}
}

Troubleshooting and Debugging
In the future, it would be helpful for you to post the request as a cURL command as well as the Catalina logs.
Your bug is in the following statement
if (member.getPassword().equals(checkIfValidOldPassword(member, password.getOldPassword()))){
// The above expression is always evaluating false
}
The member.getPassword() accessory method returns a String. However checkIfValidOldPassword method returns a boolean. Let's refactor the code for demonstration.
String pwd = member.getPassword();
String opwd = password.getOldPassword();
boolean isValud = checkIfValidOldPassword(member, opwd);
assert pwd.equals(isValid);
You are attempting to evaluate the equality of a String and a primitive boolean ( autoboxed to the Boolean wrapper class object ). Likely this statement always evaluates false thus you are returning null and not invoking the code that actually makes the update.
Autoboxing Explained
The reason this did not throw a compile time exception is due to a feature known as Autoboxing. Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes.
In your example, the equals method has a single parameter of type Object. So although you passed a primitive boolean as the first parameter in the equals method, the Java compiler converted it to an Object of type Boolean. Because Boolean is an object, and all objects inherit from Object, no exception is thrown.
Most likely you are comparing the response of ‘toString’ method on your Boolean object which returns the string “true” when the primitive boolean value corresponds with true and “false” otherwise.
Security Concerns
Please be extremely careful when you are attempting to roll your own authentication or authorization features. For the most part, a password should be salted and encrypted before storing the information at-rest. Therefore, you should only ever be able to compare one salted/encrypted string with another salted/encrypted string

Related

Jpa Repository in Spring boot app findBy issue

I'm trying to create findBy JpaRepo it's about returning only the data where isDeleted attribute is false.
this is my Service :
public List<Customer> getAllCustomers() {
List<Customer> customers = cutomerRepository.findByIsDeletedFalse();
return customers;
}
and this is my Controller :
#GetMapping("/viewList")
#CrossOrigin("http://localhost:4200/")
public ResponseEntity<List<Customer>> getAllCustomers() {
List<Customer> customers = new ArrayList<>();
customers = customerService.getAllCustomers();
if (customers.isEmpty()) {
LOGGER.error("no content ");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
LOGGER.info("calling list of customers");
return new ResponseEntity<>(customers, HttpStatus.OK);
}
and this is customer model :
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "mobile_number")
private String mobileNumber;
#Column(name = "is_deleted")
private boolean isDeleted;
}
but when I run it in postman it's not working and return an error :
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not
exist: boolean = integer Hint: No operator matches the given name
and argument types. You might need to add explicit type casts.
Position: 315
How could I solve this issue?
Looks like the name for your query isn't created right.
However, in this case, the usage of #Query will be much clearer.
Code snippet:
public interface CustomerRepo extends JpaRepository<Customer, Integer> {
List<Customer> findAllByIsDeletedIsFalse();
#Query("from Customer c where c.isDeleted=false")
List<Customer> getAllCustomers();
}
Iinstead of:
cutomerRepository.findByIsDeletedFalse()
You missed one more Is at the name of the method.
Update your Domain:
public class Customer implements Serializable {
private final static long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "serial_number")
private Long serialNumber;
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
}
JPA fields should be Objects instead of primitives. And entity class should implement Serializable as well.
If the exception will be the same you could try to update #Query:
#Query("from Customer c where c.isDeleted=0")
If pure SQL works for your DB you could use native query:
#Query(
value = "select * from Customer where is_deleted = false",
nativeQuery = true)
List<Customer> getAllCustomers();
It's not working because it doesn't follow the naming conventions for a boolean field. Usually in Java the primitive booleans are named without is prefix and the getter would be using this is prefix.
So in your case your entity class should look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
Also the naming of the spring repository method should be:
List<Customer> findAllByDeletedIsFalse();
In case you want to use a Boolean reference type you can name your field isDeleted, but then the class would look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
}
and the repository method:
List<Customer> findAllByIsDeletedIsFalse();
Boolean Java maps a bit datatype column. You are probably using int as datatype in your database.

Why UUID generate nulls instead of uniqe ID

I have service which is getting values from api and mapping it by model mapper to the unified entity.
The problem is that UUID is not working. I am getting null instead of any string id (I had to change name of if for "uniqueIdentifier" becouse objects from api had "id" field).
My entity:
#Entity
#Getter
#Setter
#Table(name = "unifiedOffers")
#AllArgsConstructor
#NoArgsConstructor
public class UnifiedOfferEntity {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "id", updatable = false, nullable = false)
private String uniqueIdentifier;
private String companyName;
private String city;
private String street;
private String title;
private LocalDateTime posted;
private String url;
}
Endpoint for tests:
#GetMapping("/test")
public String getOffers() throws ExecutionException, InterruptedException {
List<UnifiedOfferEntity> result = jobFinderService.getAllOffers();
for (UnifiedOfferEntity unifiedOfferEntity : result) {
System.out.println(unifiedOfferEntity.getUniqueIdentifier() + " " + unifiedOfferEntity.getTitle() + " " + unifiedOfferEntity.getUrl());
}
unifedOfferRepository.saveAll(result);
return String.valueOf(result.size());
}
In that foreach I am getting values like for example:
null JavaDeveloper anyLinkUrl
so only UUID is not generating ids.
have you defined the underline class to implement uuid generator?
in a similar case I used the follwing code that accepts a parameter "rangeName" you can remove
#GeneratedValue(generator="intRange")
#GenericGenerator(name="intRange",
strategy = "imp.framework.jerpBridge.IntRangeGenerator" ,
parameters = {
#Parameter( name = "rangeName", value="DOCMA01")
})
public String nrDocumento;
where
public class IntRangeGenerator implements IdentifierGenerator, Configurable {
public IntRangeGenerator(String rangeName) {
this.rangeName = rangeName;
}
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return "generated ID";
}

Validation error of type SubSelectionRequired: Sub selection required for type Timestamp of field

I use GraphQL-SPQR Library
The problem is "Validation error of type SubSelectionRequired: Sub selection required for type Timestamp"
Maybe there is expression in query for timestamp
or format in Entity
{"query":
"{findUserPointByUserId(userId:73){rowNum userAccountPointUserId totalPoint pointTypeDescription point userAccountCreatedDate} findUserAccountImgByUserId(userId:73){imageId,userId,presentImgNum}}"
}
Error
{
"errors": [
{
"message": "Validation error of type SubSelectionRequired: Sub selection required for type Timestamp of field userAccountCreatedDate",
"locations": [
{
"line": 1,
"column": 103
}
]
}
]
}
Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "view_user_account_point", schema = "public", catalog = "corus")
public class ViewUserAccountPoint {
#Id
#Basic
#GraphQLQuery(name = "rowNum")
#Column(name = "row_num", nullable = true)
private Long rowNum;
#Basic
#Column(name = "user_account_point_userid", nullable = true)
#GraphQLQuery(name = "userAccountPointUserId")
private Integer userAccountPointUserId;
#Basic
#Column(name = "subject_id", nullable = true)
#GraphQLQuery(name = "subjectId")
private Integer subjectId;
#Basic
#Column(name = "point", nullable = true)
#GraphQLQuery(name = "point")
private Integer point;
#Basic
#Column(name = "user_account_point_typeid", nullable = true)
#GraphQLQuery(name = "userAccountPointTypeId")
private Integer userAccountPointTypeId;
#Basic
#Column(name = "date_created", nullable = true)
#GraphQLQuery(name = "userAccountCreatedDate")
private Timestamp userAccountCreatedDate;
Service
public List<ViewUserAccountPoint> findUserPointByUserId(#GraphQLArgument(name = "userId") Integer userId){
return viewUserAccountPointRepository.findByUserAccountPointUserIdOrderByUserAccountCreatedDateDesc(userId);
}
Controller
private final GraphQL graphQL;
public UserController(UserAccountService userAccountService) {
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withResolverBuilders(
//Resolve by annotations
new AnnotatedResolverBuilder())
.withOperationsFromSingleton(userAccountService,UserAccountService.class)
.withValueMapperFactory(new JacksonValueMapperFactory())
.generate();
graphQL = GraphQL.newGraphQL(schema).build();
}
#PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public Map<String, Object> graphql(#RequestBody Map<String, String> request, HttpServletRequest raw) {
ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
.query(request.get("query"))
.operationName(request.get("operationName"))
.context(raw)
.build());
return executionResult.toSpecification();
}
I search through all query timestamp format
However, i couldn't find
i hope to hear the solution.
thank you
For one reason or another, Timestamp got mapped incorrectly. It ended up being an object and not a scalar.
As mentioned in the issue you opened, it's unclear where is Timestamp in your code coming from.
java.sql.Timestamp is supported out of the box in recent versions of GraphQL SPQR, so you might be on an older version.
If that's not the case, it would mean Timestamp is some other than java.sql.Timestamp, and you'd need to register a custom mapper for it.
public class TimestampMapper implements TypeMapper {
// Define the scalar as needed, see io.leangen.graphql.util.Scalars for inspiration
private static final GraphQLScalarType TIMESTAMP = ...;
#Override
public GraphQLOutputType toGraphQLType(AnnotatedType javaType, OperationMapper operationMapper, Set<Class<? extends TypeMapper>> mappersToSkip, BuildContext buildContext) {
return TIMESTAMP; //it's important to always return the same instance
}
#Override
public GraphQLInputType toGraphQLInputType(AnnotatedType javaType, OperationMapper operationMapper, Set<Class<? extends TypeMapper>> mappersToSkip, BuildContext buildContext) {
return TIMESTAMP; //same as above
}
#Override
public boolean supports(AnnotatedType type) {
return ClassUtils.isSuperClass(Timestamp.class, type);
}
}
Then register your mapper:
generator.withTypeMappers(new TimestampMapper())
It's incorrect query body for my case, make sure you have the right one.

Ebean and Play! not filtering columns with .select()

I'm trying to fetch just a part of the model using Ebean in Play! Framework, but I'm having some problems and I didn't found any solutions.
I have these models:
User:
#Entity
#Table(name = "users")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends Model{
#Id
private int id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name")
private String lastName;
#NotNull
#Column(nullable = false)
private String username;
#NotNull
#Column(nullable = false)
private String email;
private String gender;
private String locale;
private Date birthday;
private String bio;
#NotNull
#Column(nullable = false)
private boolean active;
private String avatar;
#Column(name = "created_at",nullable = false)
private Date createdAt;
#OneToMany
private List<UserToken> userTokens;
// Getters and Setters omitted for brevity
}
UserToken:
#Entity
#Table(name = "user_tokens")
public class UserToken extends Model {
#Id
private int id;
#Column(name = "user_id")
private int userId;
private String token;
#Column(name = "created_at")
#CreatedTimestamp
private Date createdAt;
#ManyToOne
private User user;
// Getters and Setters omitted for brevity
}
And then, I have a controller UserController:
public class UserController extends Controller{
public static Result list(){
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
return Results.ok(Json.toJson(user));
}
}
I expected that, when using the .select(), it would filter the fields and load a partial object, but it loads it entirely.
In the logs, there is more problems that I don't know why its happening.
It is making 3 queries. First is the one that I want. And then it makes one to fetch the whole Model, and another one to find the UserTokens. I don't know why it is doing these last two queries and I wanted just the first one to be executed.
Solution Edit
After already accepted the fact that I would have to build the Json as suggested by #biesior , I found (out of nowhere) the solution!
public static Result list() throws JsonProcessingException {
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
JsonContext jc = Ebean.createJsonContext();
return Results.ok(jc.toJsonString(user));
}
I render only the wanted fields selected in .select() after using JsonContext.
That's simple, when you using select("...") it always gets just id field (cannot be avoided - it's required for mapping) + desired fields, but if later you are trying to access the field that wasn't available in first select("...") - Ebean repeats the query and maps whole object.
In other words, you are accessing somewhere the field that wasn't available in first query, analyze your controller and/or templates, find all fields and add it to your select (even if i.e. they're commented with common HTML comment in the view!)
In the last version of Play Framework (2.6) the proper way to do this is:
public Result list() {
JsonContext json = ebeanServer.json();
List<MyClass> orders= ebeanServer.find(MyClass.class).select("id,property1,property2").findList();
return ok(json.toJson(orders));
}

Can hibernate map EnumTypes with whitespaces?

Having a hibernate mapping a legacy database I want to use EnumTypes to map certain columns that contain string constants with whitespace to some Enum class.
The mapping:
#Entity
#Table(name = "OPERATOR")
public class Operator {
#Id
#Column(name = "ID")
private Long id;
...
#Enumerated(EnumType.STRING)
#Column(name = "STATUS")
private Status status;
...
}
public enum Status {
OPERATOR_CREATED("Operator created"),
ACTIVE("Active"),
END_DATED("End dated");
private String name;
Status(String status) {
name = status;
}
}
As you can see we can't us the database values straight as the enum names, as there are white spaces in them.
I was wondering if it is possible to use enums for this?
Look at GenericEnumUserType described at hibernate.org (Under "Flexible solution")
Modify Status as follows:
public enum Status
{
OPERATOR_CREATED("Operator created"),
ACTIVE("Active"),
END_DATED("End dated");
private String name;
Status(String status)
{
name = status;
}
public String toString()
{
return name;
}
public Status fromString( String value )
{
if ( "Operator created".equals( value )
{
return OPERATOR_CREATED;
}
//etc
}
}
Now use the #Type annotation on your entity.
#Entity
#Table(name = "OPERATOR")
public class Operator {
#Id
#Column(name = "ID")
private Long id;
...
#Column(name = "STATUS", columnDefinition = "VARCHAR(31)", nullable = false )
#Type( type = "my.package.GenericEnumUserType",
parameters = {
#Parameter( name = "enumClass", value = "my.package.Status" ),
#Parameter( name = "identifierMethod", value = "toString" ),
#Parameter( name = "valueOfMethod", value = "fromString" ) } )
private Status status;
...
}
I had the same exact situation, try using replace function, something like this:
#ColumnTransformer(read = "replace(status::varchar, ' ', '')", write = "replace(?, 'End', 'End ')::status")
You have underscores so need to modify it a bit.
regexp_replace can also be handy if there are several enum values with spaces.
Note, I am using postgresql 9.6

Categories

Resources