Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
minerva
core
Commits
8a812cdb
Commit
8a812cdb
authored
Jun 19, 2020
by
Piotr Gawron
Browse files
API call to request reset password implemented
parent
09618f2c
Changes
11
Hide whitespace changes
Inline
Side-by-side
model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java
View file @
8a812cdb
...
...
@@ -358,6 +358,9 @@ public enum ConfigurationElementType {
DAPI_PASSWORD
(
"Password to Data-API system"
,
""
,
ConfigurationElementEditType
.
PASSWORD
,
false
,
ConfigurationElementTypeGroup
.
DAPI
),
MINERVA_ROOT
(
"Minerva root url"
,
""
,
ConfigurationElementEditType
.
URL
,
false
,
ConfigurationElementTypeGroup
.
SERVER_CONFIGURATION
),
;
/**
...
...
model/src/main/java/lcsb/mapviewer/model/user/ResetPasswordToken.java
0 → 100644
View file @
8a812cdb
package
lcsb.mapviewer.model.user
;
import
java.io.Serializable
;
import
java.util.Calendar
;
import
javax.persistence.*
;
@Entity
public
class
ResetPasswordToken
implements
Serializable
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Integer
id
;
@Column
(
nullable
=
false
)
private
String
token
;
@ManyToOne
private
User
user
;
@Column
(
nullable
=
false
)
private
Calendar
expires
;
public
ResetPasswordToken
(
User
user
,
String
token
,
Calendar
expires
)
{
this
.
token
=
token
;
this
.
user
=
user
;
this
.
expires
=
expires
;
}
public
String
getToken
()
{
return
token
;
}
public
User
getUser
()
{
return
user
;
}
public
Calendar
getExpires
()
{
return
expires
;
}
}
persist/src/main/java/lcsb/mapviewer/persist/dao/user/ResetPasswordTokenDao.java
0 → 100644
View file @
8a812cdb
package
lcsb.mapviewer.persist.dao.user
;
import
org.springframework.stereotype.Repository
;
import
lcsb.mapviewer.model.user.ResetPasswordToken
;
import
lcsb.mapviewer.persist.dao.BaseDao
;
@Repository
public
class
ResetPasswordTokenDao
extends
BaseDao
<
ResetPasswordToken
>
{
public
ResetPasswordTokenDao
()
{
super
(
ResetPasswordToken
.
class
);
}
public
ResetPasswordToken
getByToken
(
String
token
)
{
return
getByParameter
(
"token"
,
token
);
}
}
persist/src/main/resources/db/migration/15.1.0~alpha.0/V15.0.1.20200619__reset_password_token.sql
0 → 100644
View file @
8a812cdb
CREATE
SEQUENCE
reset_password_token_sequence
INCREMENT
1
MINVALUE
1
MAXVALUE
9223372036854775807
START
1
CACHE
1
;
CREATE
TABLE
reset_password_token_table
(
id
integer
NOT
NULL
DEFAULT
nextval
(
'reset_password_token_sequence'
::
regclass
),
token
varchar
NOT
NULL
,
user_id
integer
not
null
,
expires
timestamp
without
time
zone
,
CONSTRAINT
reset_password_token_pk
PRIMARY
KEY
(
id
),
CONSTRAINT
reset_password_token_user_fk
FOREIGN
KEY
(
user_id
)
REFERENCES
user_table
(
id
)
MATCH
SIMPLE
ON
UPDATE
NO
ACTION
ON
DELETE
NO
ACTION
)
WITH
(
OIDS
=
FALSE
);
rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java
View file @
8a812cdb
...
...
@@ -4,6 +4,8 @@ import java.io.IOException;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
javax.mail.MessagingException
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.springframework.beans.factory.annotation.Autowired
;
...
...
@@ -107,6 +109,25 @@ public class UserController extends BaseController {
return
userRest
.
removeUser
(
login
);
}
@PostMapping
(
value
=
"/{login}:requestResetPassword"
)
public
Map
<
String
,
Object
>
requestResetPasswordToken
(
@PathVariable
(
value
=
"login"
)
String
login
)
throws
QueryException
,
MessagingException
{
userRest
.
requestResetPassword
(
login
);
Map
<
String
,
Object
>
result
=
new
HashMap
<>();
result
.
put
(
"status"
,
"OK"
);
return
result
;
}
@PostMapping
(
value
=
":resetPassword"
)
public
Map
<
String
,
Object
>
resetPassword
(
@PathVariable
(
value
=
"token"
)
String
token
,
String
password
)
throws
IOException
,
QueryException
{
userRest
.
resetPassword
(
token
,
password
);
Map
<
String
,
Object
>
result
=
new
HashMap
<>();
result
.
put
(
"status"
,
"OK"
);
return
result
;
}
public
IUserService
getUserService
()
{
return
userService
;
}
...
...
rest-api/src/main/java/lcsb/mapviewer/api/users/UserRestImpl.java
View file @
8a812cdb
...
...
@@ -2,6 +2,8 @@ package lcsb.mapviewer.api.users;
import
java.util.*
;
import
javax.mail.MessagingException
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.springframework.beans.factory.annotation.Autowired
;
...
...
@@ -15,12 +17,13 @@ import org.springframework.util.MultiValueMap;
import
lcsb.mapviewer.api.*
;
import
lcsb.mapviewer.common.Configuration
;
import
lcsb.mapviewer.common.exception.
InvalidArgumentException
;
import
lcsb.mapviewer.common.exception.
*
;
import
lcsb.mapviewer.model.map.MiriamType
;
import
lcsb.mapviewer.model.security.Privilege
;
import
lcsb.mapviewer.model.security.PrivilegeType
;
import
lcsb.mapviewer.model.user.*
;
import
lcsb.mapviewer.model.user.annotator.*
;
import
lcsb.mapviewer.services.utils.EmailSender
;
@Transactional
@Service
...
...
@@ -33,9 +36,12 @@ public class UserRestImpl extends BaseRestImpl {
private
Logger
logger
=
LogManager
.
getLogger
(
UserRestImpl
.
class
);
private
PasswordEncoder
passwordEncoder
;
private
EmailSender
emailSender
;
@Autowired
public
UserRestImpl
(
PasswordEncoder
passwordEncoder
)
{
public
UserRestImpl
(
PasswordEncoder
passwordEncoder
,
EmailSender
emailSender
)
{
this
.
passwordEncoder
=
passwordEncoder
;
this
.
emailSender
=
emailSender
;
}
public
Map
<
String
,
Object
>
getUser
(
String
login
,
String
columns
)
...
...
@@ -731,4 +737,28 @@ public class UserRestImpl extends BaseRestImpl {
return
okStatus
();
}
public
void
requestResetPassword
(
String
login
)
throws
MessagingException
,
QueryException
{
User
user
=
getUserService
().
getUserByLogin
(
login
);
if
(
user
==
null
)
{
throw
new
ObjectNotFoundException
(
"User does not exist"
);
}
if
(
user
.
getEmail
()
==
null
||
user
.
getEmail
().
equals
(
""
))
{
throw
new
QueryException
(
"User does not have email address defined"
);
}
if
(
getConfigurationService
().
getConfigurationValue
(
ConfigurationElementType
.
MINERVA_ROOT
).
trim
().
isEmpty
())
{
throw
new
InvalidStateException
(
"Cannot create token - minerva root url is not defined"
);
}
if
(!
emailSender
.
canSendEmails
())
{
throw
new
InvalidStateException
(
"Cannot create token - minerva cannot send emails"
);
}
getUserService
().
createResetPasswordToken
(
user
);
}
public
void
resetPassword
(
String
token
,
String
password
)
{
throw
new
NotImplementedException
();
}
}
service/src/main/java/lcsb/mapviewer/services/impl/UserService.java
View file @
8a812cdb
package
lcsb.mapviewer.services.impl
;
import
java.awt.
*
;
import
java.awt.
Color
;
import
java.util.*
;
import
java.util.List
;
import
javax.mail.MessagingException
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
...
...
@@ -18,31 +19,40 @@ import lcsb.mapviewer.model.security.Privilege;
import
lcsb.mapviewer.model.security.PrivilegeType
;
import
lcsb.mapviewer.model.user.*
;
import
lcsb.mapviewer.persist.dao.ProjectDao
;
import
lcsb.mapviewer.persist.dao.user.ResetPasswordTokenDao
;
import
lcsb.mapviewer.persist.dao.user.UserDao
;
import
lcsb.mapviewer.services.interfaces.*
;
import
lcsb.mapviewer.services.utils.EmailSender
;
@Transactional
@Service
public
class
UserService
implements
IUserService
{
private
static
Logger
logger
=
LogManager
.
getLogger
(
UserService
.
class
);
private
UserDao
userDao
;
private
ILdapService
ldapService
;
private
IConfigurationService
configurationService
;
private
IPrivilegeService
privilegeService
;
private
ProjectDao
projectDao
;
private
ResetPasswordTokenDao
resetPasswordTokenDao
;
private
EmailSender
emailSender
;
@Autowired
public
UserService
(
UserDao
userDao
,
ILdapService
ldapService
,
IConfigurationService
configurationService
,
IPrivilegeService
privilegeService
,
ProjectDao
projectDao
)
{
ProjectDao
projectDao
,
ResetPasswordTokenDao
resetPasswordTokenDao
,
EmailSender
emailSender
)
{
this
.
userDao
=
userDao
;
this
.
ldapService
=
ldapService
;
this
.
configurationService
=
configurationService
;
this
.
privilegeService
=
privilegeService
;
this
.
projectDao
=
projectDao
;
this
.
resetPasswordTokenDao
=
resetPasswordTokenDao
;
this
.
emailSender
=
emailSender
;
}
@Override
...
...
@@ -191,4 +201,26 @@ public class UserService implements IUserService {
}
}
@Override
public
void
createResetPasswordToken
(
User
user
)
throws
MessagingException
{
Calendar
expires
=
Calendar
.
getInstance
();
expires
.
add
(
Calendar
.
DAY_OF_MONTH
,
1
);
ResetPasswordToken
token
=
new
ResetPasswordToken
(
user
,
UUID
.
randomUUID
().
toString
(),
expires
);
resetPasswordTokenDao
.
add
(
token
);
String
content
=
"Dear "
+
user
.
getName
()
+
" "
+
user
.
getSurname
()
+
" ("
+
user
.
getLogin
()
+
"),<br/><br/>"
+
"We received request to reset your password. You can reset the password using this <a href=\""
+
configurationService
.
getConfigurationValue
(
ConfigurationElementType
.
MINERVA_ROOT
)
+
"login.xhtml?resetPasswordToken="
+
token
.
getToken
()
+
"\">link</a>. Link will be valid for the next 24 hours.<br/><br/>"
+
"If you did not perform this action you can ignore the email.<br/>"
;
try
{
emailSender
.
sendEmail
(
"Minerva - Reset password"
,
content
,
user
.
getEmail
());
}
catch
(
MessagingException
e
)
{
resetPasswordTokenDao
.
delete
(
token
);
throw
e
;
}
}
}
service/src/main/java/lcsb/mapviewer/services/interfaces/IUserService.java
View file @
8a812cdb
...
...
@@ -2,6 +2,8 @@ package lcsb.mapviewer.services.interfaces;
import
java.util.*
;
import
javax.mail.MessagingException
;
import
lcsb.mapviewer.commands.ColorExtractor
;
import
lcsb.mapviewer.model.security.PrivilegeType
;
import
lcsb.mapviewer.model.user.User
;
...
...
@@ -55,4 +57,6 @@ public interface IUserService {
ColorExtractor
getColorExtractorForUser
(
User
user
);
Map
<
String
,
Boolean
>
ldapAccountExistsForLogin
(
Collection
<
User
>
logins
);
void
createResetPasswordToken
(
User
user
)
throws
MessagingException
;
}
\ No newline at end of file
service/src/main/java/lcsb/mapviewer/services/utils/EmailSender.java
View file @
8a812cdb
...
...
@@ -11,6 +11,9 @@ import javax.mail.internet.*;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
com.sun.mail.smtp.SMTPTransport
;
...
...
@@ -25,6 +28,8 @@ import lcsb.mapviewer.services.interfaces.IConfigurationService;
* @author Piotr Gawron
*
*/
@Transactional
@Service
public
class
EmailSender
{
/**
...
...
@@ -99,6 +104,7 @@ public class EmailSender {
* configuration service that contains information required to access
* email account
*/
@Autowired
public
EmailSender
(
IConfigurationService
configurationService
)
{
this
(
configurationService
.
getConfigurationValue
(
ConfigurationElementType
.
EMAIL_ADDRESS
),
configurationService
.
getConfigurationValue
(
ConfigurationElementType
.
EMAIL_LOGIN
),
...
...
@@ -283,7 +289,7 @@ public class EmailSender {
* @param emailAddress
* email address that should receive email
* @throws MessagingException
* thrown when
e
there is a problem with sending email
* thrown when there is a problem with sending email
*/
public
void
sendEmail
(
String
subject
,
String
message
,
String
emailAddress
)
throws
MessagingException
{
if
(
emailAddress
==
null
||
emailAddress
.
equals
(
""
))
{
...
...
@@ -296,4 +302,11 @@ public class EmailSender {
}
public
boolean
canSendEmails
()
{
if
(
smtpHost
.
equals
(
ConfigurationElementType
.
EMAIL_SMTP_SERVER
.
getDefaultValue
()))
{
return
false
;
}
return
true
;
}
}
web/src/test/java/lcsb/mapviewer/web/EmailSenderMockConfiguration.java
0 → 100644
View file @
8a812cdb
package
lcsb.mapviewer.web
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.mockito.Mockito
;
import
org.springframework.context.annotation.*
;
import
lcsb.mapviewer.services.utils.EmailSender
;
@Profile
(
"emailSenderProfile"
)
@Configuration
public
class
EmailSenderMockConfiguration
{
Logger
logger
=
LogManager
.
getLogger
();
@Bean
@Primary
public
EmailSender
emailSender
()
throws
Exception
{
EmailSender
mock
=
Mockito
.
mock
(
EmailSender
.
class
);
Mockito
.
doReturn
(
true
).
when
(
mock
).
canSendEmails
();
return
mock
;
}
}
web/src/test/java/lcsb/mapviewer/web/UserControllerIntegrationTest.java
View file @
8a812cdb
...
...
@@ -14,10 +14,12 @@ import org.apache.logging.log4j.Logger;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.mockito.Mockito
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.http.MediaType
;
import
org.springframework.mock.web.MockHttpSession
;
import
org.springframework.test.annotation.Rollback
;
import
org.springframework.test.context.ActiveProfiles
;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
;
import
org.springframework.test.web.servlet.RequestBuilder
;
import
org.springframework.transaction.annotation.Transactional
;
...
...
@@ -30,10 +32,13 @@ import lcsb.mapviewer.model.security.PrivilegeType;
import
lcsb.mapviewer.model.user.ConfigurationElementType
;
import
lcsb.mapviewer.model.user.User
;
import
lcsb.mapviewer.persist.dao.ProjectDao
;
import
lcsb.mapviewer.persist.dao.user.ResetPasswordTokenDao
;
import
lcsb.mapviewer.services.interfaces.IConfigurationService
;
import
lcsb.mapviewer.services.interfaces.IUserService
;
import
lcsb.mapviewer.services.utils.EmailSender
;
@RunWith
(
SpringJUnit4ClassRunner
.
class
)
@ActiveProfiles
(
"emailSenderProfile"
)
@Transactional
@Rollback
public
class
UserControllerIntegrationTest
extends
ControllerIntegrationTest
{
...
...
@@ -56,6 +61,12 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
@Autowired
private
ProjectDao
projectDao
;
@Autowired
private
ResetPasswordTokenDao
resetPasswordTokenDao
;
@Autowired
private
EmailSender
emailSender
;
@Before
public
void
setup
()
{
user
=
createUser
(
TEST_USER_LOGIN
,
TEST_USER_PASSWORD
);
...
...
@@ -438,4 +449,79 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.
andExpect
(
status
().
isForbidden
());
}
@Test
public
void
requestResetPassword
()
throws
Exception
{
long
count
=
resetPasswordTokenDao
.
getCount
();
configureServerForResetPasswordRequest
();
RequestBuilder
grantRequest
=
post
(
"/users/"
+
BUILT_IN_TEST_ADMIN_LOGIN
+
":requestResetPassword"
);
mockMvc
.
perform
(
grantRequest
)
.
andExpect
(
status
().
is2xxSuccessful
());
assertEquals
(
count
+
1
,
resetPasswordTokenDao
.
getCount
());
}
@Test
public
void
requestResetPasswordWhenMinervaRootNotConfigured
()
throws
Exception
{
configureServerForResetPasswordRequest
();
configurationService
.
setConfigurationValue
(
ConfigurationElementType
.
MINERVA_ROOT
,
""
);
RequestBuilder
grantRequest
=
post
(
"/users/"
+
BUILT_IN_TEST_ADMIN_LOGIN
+
":requestResetPassword"
);
mockMvc
.
perform
(
grantRequest
)
.
andExpect
(
status
().
is5xxServerError
());
}
@Test
public
void
requestResetPasswordWhenMinervaCannotSendMails
()
throws
Exception
{
try
{
configureServerForResetPasswordRequest
();
RequestBuilder
grantRequest
=
post
(
"/users/"
+
BUILT_IN_TEST_ADMIN_LOGIN
+
":requestResetPassword"
);
Mockito
.
doReturn
(
false
).
when
(
emailSender
).
canSendEmails
();
mockMvc
.
perform
(
grantRequest
)
.
andExpect
(
status
().
is5xxServerError
());
}
finally
{
Mockito
.
doReturn
(
true
).
when
(
emailSender
).
canSendEmails
();
}
}
@Test
public
void
requestResetPasswordWhenUserWithoutEmail
()
throws
Exception
{
configureServerForResetPasswordRequest
();
User
user
=
userService
.
getUserByLogin
(
BUILT_IN_TEST_ADMIN_LOGIN
);
user
.
setEmail
(
""
);
userService
.
updateUser
(
user
);
RequestBuilder
grantRequest
=
post
(
"/users/"
+
BUILT_IN_TEST_ADMIN_LOGIN
+
":requestResetPassword"
);
mockMvc
.
perform
(
grantRequest
)
.
andExpect
(
status
().
is4xxClientError
());
}
@Test
public
void
requestResetPasswordForUnknownUser
()
throws
Exception
{
configureServerForResetPasswordRequest
();
RequestBuilder
grantRequest
=
post
(
"/users/blabla:requestResetPassword"
);
mockMvc
.
perform
(
grantRequest
)
.
andExpect
(
status
().
is4xxClientError
());
}
private
void
configureServerForResetPasswordRequest
()
{
User
user
=
userService
.
getUserByLogin
(
BUILT_IN_TEST_ADMIN_LOGIN
);
user
.
setEmail
(
"test@test.xyz"
);
userService
.
updateUser
(
user
);
configurationService
.
setConfigurationValue
(
ConfigurationElementType
.
MINERVA_ROOT
,
"http://localhost:8080/minerva/"
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment