Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Genesys PGR
Genesys Backend
Commits
3211dc28
Commit
3211dc28
authored
Jan 15, 2014
by
igoshin
Browse files
Final version of user story "Email validation"
#10141
parent
12860fb1
Changes
21
Hide whitespace changes
Inline
Side-by-side
pom.xml
View file @
3211dc28
...
...
@@ -352,7 +352,14 @@
<artifactId>
spring-aspects
</artifactId>
<version>
3.2.5.RELEASE
</version>
</dependency>
</dependencies>
<dependency>
<groupId>
javax.mail
</groupId>
<artifactId>
mail
</artifactId>
<version>
1.5.0-b01
</version>
</dependency>
</dependencies>
<build>
<plugins>
...
...
src/main/java/org/genesys2/server/service/impl/EMailServiceImpl.java
View file @
3211dc28
/**
* Copyright 2013 Global Crop Diversity Trust
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...
...
@@ -17,16 +17,94 @@
package
org.genesys2.server.service.impl
;
import
org.genesys2.server.service.EMailService
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.mail.javamail.JavaMailSender
;
import
org.springframework.mail.javamail.MimeMessageHelper
;
import
org.springframework.mail.javamail.MimeMessagePreparator
;
import
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
;
import
org.springframework.stereotype.Service
;
import
javax.mail.internet.MimeMessage
;
import
java.util.Arrays
;
@Service
public
class
EMailServiceImpl
implements
EMailService
{
// TODO get SMTP properties from properties file
@Override
public
void
sendMail
(
String
email
,
String
name
,
String
mailSubject
,
String
mailBody
)
{
// TODO Auto-generated method stub
private
Logger
_logger
=
LoggerFactory
.
getLogger
(
getClass
());
@Autowired
private
JavaMailSender
mailSender
;
@Autowired
private
ThreadPoolTaskExecutor
executor
;
@Value
(
"${mail.async}"
)
private
boolean
async
;
@Value
(
"${mail.debug.message}"
)
private
String
debugMessage
;
@Value
(
"${mail.user.from}"
)
private
String
emailFrom
;
@Override
public
void
sendMail
(
String
email
,
String
name
,
String
mailSubject
,
String
mailBody
)
{
sendSimpleEmail
(
mailSubject
,
mailBody
,
emailFrom
,
email
);
}
public
void
sendSimpleEmail
(
final
String
subject
,
final
String
text
,
final
String
emailFrom
,
final
String
...
emailTo
)
{
printDebugInfo
(
subject
,
text
,
emailFrom
,
emailTo
);
final
MimeMessagePreparator
preparator
=
new
MimeMessagePreparator
()
{
@SuppressWarnings
(
"unchecked"
)
@Override
public
void
prepare
(
MimeMessage
mimeMessage
)
throws
Exception
{
MimeMessageHelper
message
=
new
MimeMessageHelper
(
mimeMessage
,
"UTF-8"
);
message
.
setFrom
(
emailFrom
);
message
.
setTo
(
emailTo
);
message
.
setSubject
(
subject
);
message
.
setText
(
text
,
true
);
}
};
doSend
(
preparator
);
}
protected
void
doSend
(
final
MimeMessagePreparator
preparator
)
{
//execute sender in separate thread
if
(
async
)
{
executor
.
submit
(
new
Runnable
()
{
@Override
public
void
run
()
{
try
{
mailSender
.
send
(
preparator
);
}
catch
(
Exception
e
)
{
_logger
.
error
(
e
.
getMessage
(),
e
);
}
}
});
}
else
{
mailSender
.
send
(
preparator
);
}
}
protected
void
printDebugInfo
(
String
subject
,
String
text
,
String
emailFrom
,
String
[]
emailTo
)
{
System
.
out
.
println
(
getDebugString
(
subject
,
text
,
emailFrom
,
emailTo
));
}
}
protected
String
getDebugString
(
String
subject
,
String
text
,
String
emailFrom
,
String
[]
emailTo
)
{
return
String
.
format
(
debugMessage
,
emailFrom
,
Arrays
.
toString
(
emailTo
),
subject
,
text
);
}
}
src/main/java/org/genesys2/server/service/impl/EMailVerificationServiceImpl.java
View file @
3211dc28
/**
* Copyright 2013 Global Crop Diversity Trust
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...
...
@@ -16,9 +16,6 @@
package
org.genesys2.server.service.impl
;
import
java.text.MessageFormat
;
import
java.util.UUID
;
import
org.apache.commons.lang.RandomStringUtils
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
...
...
@@ -33,66 +30,68 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
java.text.MessageFormat
;
import
java.util.UUID
;
@Service
@Transactional
(
readOnly
=
true
)
public
class
EMailVerificationServiceImpl
implements
EMailVerificationService
{
private
static
final
Log
LOG
=
LogFactory
.
getLog
(
EMailVerificationServiceImpl
.
class
);
@Autowired
private
VerificationTokenRepository
verificationTokenRepository
;
@Autowired
private
EMailService
emailService
;
@Autowired
private
UserService
userService
;
@Transactional
public
void
sendVerificationEmail
(
User
user
)
{
// Generate new token
VerificationToken
verificationToken
=
generateToken
(
"email-validation"
,
user
.
getUuid
());
// TODO use content services to generate email body
String
mailSubject
=
"Genesys account verification"
;
String
mailBody
=
MessageFormat
.
format
(
"Click http://sandbox.genesys-pgr.org/profile/{0}/validate and enter key {1}"
,
verificationToken
.
getUuid
(),
verificationToken
.
getKey
());
emailService
.
sendMail
(
user
.
getEmail
(),
user
.
getName
(),
mailSubject
,
mailBody
);
}
private
VerificationToken
generateToken
(
String
tokenPurpose
,
String
dataUuid
)
{
VerificationToken
token
=
new
VerificationToken
();
token
.
setPurpose
(
tokenPurpose
);
// Store data
token
.
setData
(
dataUuid
);
token
.
setUuid
(
UUID
.
nameUUIDFromBytes
(
dataUuid
.
getBytes
()).
toString
());
token
.
setKey
(
RandomStringUtils
.
random
(
4
));
verificationTokenRepository
.
save
(
token
);
return
token
;
}
@Override
@Transactional
public
boolean
validateEMail
(
String
tokenUuid
,
String
key
)
{
VerificationToken
verificationToken
=
verificationTokenRepository
.
findByUuidAndKey
(
tokenUuid
,
key
);
if
(
verificationToken
==
null
)
{
LOG
.
warn
(
"No such verification token "
+
tokenUuid
+
" key="
+
key
);
return
false
;
}
try
{
userService
.
userEmailValidated
(
verificationToken
.
getData
());
// FIXME Move to finally?
// Remove token
verificationTokenRepository
.
delete
(
verificationToken
);
return
true
;
}
catch
(
UserException
e
)
{
LOG
.
error
(
e
.
getMessage
(),
e
);
}
return
false
;
}
private
static
final
Log
LOG
=
LogFactory
.
getLog
(
EMailVerificationServiceImpl
.
class
);
@Autowired
private
VerificationTokenRepository
verificationTokenRepository
;
@Autowired
private
EMailService
emailService
;
@Autowired
private
UserService
userService
;
@Transactional
public
void
sendVerificationEmail
(
User
user
)
{
// Generate new token
VerificationToken
verificationToken
=
generateToken
(
"email-validation"
,
user
.
getUuid
());
String
mailSubject
=
"Genesys account verification"
;
String
mailBody
=
MessageFormat
.
format
(
"Click http://sandbox.genesys-pgr.org/profile/{0}/validate and enter key {1}"
,
verificationToken
.
getUuid
(),
verificationToken
.
getKey
());
emailService
.
sendMail
(
user
.
getEmail
(),
user
.
getName
(),
mailSubject
,
mailBody
);
}
private
VerificationToken
generateToken
(
String
tokenPurpose
,
String
dataUuid
)
{
VerificationToken
token
=
new
VerificationToken
();
token
.
setPurpose
(
tokenPurpose
);
// Store data
token
.
setData
(
dataUuid
);
token
.
setUuid
(
UUID
.
nameUUIDFromBytes
(
dataUuid
.
getBytes
()).
toString
());
token
.
setKey
(
RandomStringUtils
.
randomAlphanumeric
(
4
));
verificationTokenRepository
.
save
(
token
);
return
token
;
}
@Override
@Transactional
public
boolean
validateEMail
(
String
tokenUuid
,
String
key
)
{
VerificationToken
verificationToken
=
verificationTokenRepository
.
findByUuidAndKey
(
tokenUuid
,
key
);
if
(
verificationToken
==
null
)
{
LOG
.
warn
(
"No such verification token "
+
tokenUuid
+
" key="
+
key
);
return
false
;
}
try
{
userService
.
userEmailValidated
(
verificationToken
.
getData
());
// Remove token
verificationTokenRepository
.
delete
(
verificationToken
);
return
true
;
}
catch
(
UserException
e
)
{
LOG
.
error
(
e
.
getMessage
(),
e
);
}
return
false
;
}
}
src/main/java/org/genesys2/server/service/impl/UserServiceImpl.java
View file @
3211dc28
...
...
@@ -16,11 +16,6 @@
package
org.genesys2.server.service.impl
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.genesys2.server.exception.NoUserFoundException
;
...
...
@@ -40,11 +35,21 @@ import org.springframework.data.domain.PageImpl;
import
org.springframework.data.domain.PageRequest
;
import
org.springframework.data.domain.Pageable
;
import
org.springframework.security.access.prepost.PreAuthorize
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.SimpleGrantedAuthority
;
import
org.springframework.security.core.context.SecurityContextHolder
;
import
org.springframework.security.core.userdetails.UsernameNotFoundException
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
@Service
@Transactional
(
readOnly
=
true
)
public
class
UserServiceImpl
implements
UserService
{
...
...
@@ -296,7 +301,21 @@ public class UserServiceImpl implements UserService {
}
// Since it's a set, we can just add
userRoles
.
add
(
UserRole
.
VALIDATEDUSER
);
updateUser
(
user
);
addRole
(
UserRole
.
VALIDATEDUSER
.
getName
());
updateUser
(
user
);
LOG
.
info
(
"Ensured VALIDATEDUSER role for user "
+
user
);
}
private
void
addRole
(
String
role
){
List
<
GrantedAuthority
>
authorities
=
new
ArrayList
<>();
SimpleGrantedAuthority
simpleGrantedAuthority
=
new
SimpleGrantedAuthority
(
role
);
authorities
.
add
(
simpleGrantedAuthority
);
Object
principal
=
SecurityContextHolder
.
getContext
().
getAuthentication
().
getPrincipal
();
Authentication
authentication
=
new
UsernamePasswordAuthenticationToken
(
principal
,
principal
,
authorities
);
SecurityContextHolder
.
getContext
().
setAuthentication
(
authentication
);
}
}
src/main/java/org/genesys2/server/servlet/controller/UserProfileController.java
View file @
3211dc28
/**
* Copyright 2013 Global Crop Diversity Trust
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...
...
@@ -27,6 +27,7 @@ import org.genesys2.spring.ResourceNotFoundException;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.security.access.prepost.PreAuthorize
;
import
org.springframework.security.authentication.AuthenticationManager
;
import
org.springframework.stereotype.Controller
;
import
org.springframework.ui.ModelMap
;
import
org.springframework.validation.Validator
;
...
...
@@ -39,100 +40,117 @@ import org.springframework.web.bind.annotation.RequestParam;
@RequestMapping
(
"/profile"
)
public
class
UserProfileController
extends
BaseController
{
@Autowired
private
UserService
userService
;
@Autowired
private
Validator
validator
;
@Autowired
private
TeamService
teamService
;
@Autowired
private
ContentService
contentService
;
@Autowired
private
EMailVerificationService
emailVerificationService
;
@Value
(
"${captcha.privateKey}"
)
private
String
captchaPrivateKey
;
@Value
(
"${captcha.publicKey}"
)
private
String
captchaPublicKey
;
@RequestMapping
@PreAuthorize
(
"isAuthenticated()"
)
public
String
welcome
(
ModelMap
model
)
{
User
user
=
userService
.
getMe
();
return
"redirect:/profile/"
+
user
.
getUuid
();
}
@RequestMapping
(
"/{uuid:.+}"
)
public
String
someProfile
(
ModelMap
model
,
@PathVariable
(
"uuid"
)
String
uuid
)
{
User
user
=
userService
.
getUserByUuid
(
uuid
);
if
(
user
==
null
)
{
throw
new
ResourceNotFoundException
();
}
model
.
addAttribute
(
"user"
,
user
);
model
.
addAttribute
(
"teams"
,
teamService
.
listUserTeams
(
user
));
return
"/user/profile"
;
}
@RequestMapping
(
"/{uuid:.+}/edit"
)
@PreAuthorize
(
"hasRole('ADMINISTRATOR') || principal.user.email == #email"
)
public
String
edit
(
ModelMap
model
,
@PathVariable
(
"uuid"
)
String
uuid
)
{
someProfile
(
model
,
uuid
);
return
"/user/edit"
;
}
@RequestMapping
(
value
=
"/{tokenUuid:.+}/validate"
,
method
=
RequestMethod
.
GET
)
public
String
validateEmail
(
ModelMap
model
,
@PathVariable
(
"tokenUuid"
)
String
tokenUuid
)
{
// TODO Fill model
// TODO Fix /WEB-INF/jsp/user/validateemail.jsp
return
"/user/validateemail"
;
}
@RequestMapping
(
value
=
"/{tokenUuid:.+}/validate"
,
method
=
RequestMethod
.
POST
)
public
String
validateEmail2
(
ModelMap
model
,
@PathVariable
(
"tokenUuid"
)
String
tokenUuid
,
@RequestParam
(
"key"
)
String
key
)
{
if
(
emailVerificationService
.
validateEMail
(
tokenUuid
,
key
))
{
// Valid
// TODO Change current security context if user is logged in
// redirect
return
"redirect:/profile"
;
}
else
{
// Not valid
return
"/user/validateemail"
;
}
}
@PreAuthorize
(
"isAuthenticated()"
)
@RequestMapping
(
value
=
"/update"
,
method
=
{
RequestMethod
.
POST
})
public
String
updateMe
(
ModelMap
model
,
@RequestParam
(
"name"
)
String
name
,
@RequestParam
(
"pwd1"
)
String
pwd1
,
@RequestParam
(
"pwd2"
)
String
pwd2
)
{
User
user
=
userService
.
getMe
();
if
(
user
==
null
)
{
throw
new
ResourceNotFoundException
();
}
userService
.
updateData
(
user
.
getId
(),
name
);
if
(
StringUtils
.
isNotBlank
(
pwd1
))
{
if
(
pwd1
.
equals
(
pwd2
))
{
try
{
_logger
.
info
(
"Updating password for "
+
user
);
userService
.
updatePassword
(
user
.
getId
(),
pwd1
);
_logger
.
warn
(
"Password updated for "
+
user
);
}
catch
(
UserException
e
)
{
_logger
.
error
(
e
.
getMessage
(),
e
);
}
}
else
{
_logger
.
warn
(
"Passwords didn't match for "
+
user
);
}
}
return
"redirect:/profile"
;
}
@Autowired
private
AuthenticationManager
authenticationManager
;
@Autowired
private
UserService
userService
;
@Autowired
private
Validator
validator
;
@Autowired
private
TeamService
teamService
;
@Autowired
private
ContentService
contentService
;
@Autowired
private
EMailVerificationService
emailVerificationService
;
@Value
(
"${captcha.privateKey}"
)
private
String
captchaPrivateKey
;
@Value
(
"${captcha.publicKey}"
)
private
String
captchaPublicKey
;
@RequestMapping
@PreAuthorize
(
"isAuthenticated()"
)
public
String
welcome
(
ModelMap
model
)
{
User
user
=
userService
.
getMe
();
return
"redirect:/profile/"
+
user
.
getUuid
();
}
@RequestMapping
(
"/{uuid:.+}"
)
public
String
someProfile
(
ModelMap
model
,
@PathVariable
(
"uuid"
)
String
uuid
)
{
User
user
=
userService
.
getUserByUuid
(
uuid
);
if
(
user
==
null
)
{
throw
new
ResourceNotFoundException
();
}
model
.
addAttribute
(
"user"
,
user
);
model
.
addAttribute
(
"teams"
,
teamService
.
listUserTeams
(
user
));
return
"/user/profile"
;
}
@RequestMapping
(
"/{uuid:.+}/edit"
)
@PreAuthorize
(
"hasRole('ADMINISTRATOR') || principal.user.email == #email"
)
public
String
edit
(
ModelMap
model
,
@PathVariable
(
"uuid"
)
String
uuid
)
{
someProfile
(
model
,
uuid
);
return
"/user/edit"
;
}
@RequestMapping
(
value
=
"/{tokenUuid:.+}/validate"
,
method
=
RequestMethod
.
GET
)
public
String
validateEmail
(
ModelMap
model
,
@PathVariable
(
"tokenUuid"
)
String
tokenUuid
)
{
model
.
addAttribute
(
"tokenUuid"
,
tokenUuid
);
return
"/user/validateemail"
;
}
@RequestMapping
(
value
=
"/{uuid}/send"
,
method
=
RequestMethod
.
GET
)
public
String
sendEmail
(
ModelMap
model
,
@PathVariable
(
"uuid"
)
String
uuid
)
{
User
user
=
userService
.
getUserByUuid
(
uuid
);
emailVerificationService
.
sendVerificationEmail
(
user
);
model
.
addAttribute
(
"tokenUuid"
,
uuid
);
return
"redirect:/profile/"
+
user
.
getUuid
();
}
@RequestMapping
(
value
=
"/{tokenUuid:.+}/validate"
,
method
=
RequestMethod
.
POST
)
public
String
validateEmail2
(
ModelMap
model
,
@PathVariable
(
"tokenUuid"
)
String
tokenUuid
,
@RequestParam
(
"key"
)
String
key
)
{
if
(
emailVerificationService
.
validateEMail
(
tokenUuid
,
key
))
{
// Valid
return
"redirect:/profile"
;
}
else
{
// Not valid
model
.
addAttribute
(
"tokenUuid"
,
tokenUuid
);
model
.
addAttribute
(
"error"
,
"error"
);
return
"/user/validateemail"
;
}
}
@PreAuthorize
(
"isAuthenticated()"
)
@RequestMapping
(
value
=
"/update"
,
method
=
{
RequestMethod
.
POST
})
public
String
updateMe
(
ModelMap
model
,
@RequestParam
(
"name"
)
String
name
,
@RequestParam
(
"pwd1"
)
String
pwd1
,
@RequestParam
(
"pwd2"
)
String
pwd2
)
{
User
user
=
userService
.
getMe
();
if
(
user
==
null
)
{
throw
new
ResourceNotFoundException
();
}
userService
.
updateData
(
user
.
getId
(),
name
);
if
(
StringUtils
.
isNotBlank
(
pwd1
))
{
if
(
pwd1
.
equals
(
pwd2
))
{
try
{
_logger
.
info
(
"Updating password for "
+
user
);
userService
.
updatePassword
(
user
.
getId
(),
pwd1
);
_logger
.
warn
(
"Password updated for "
+
user
);
}
catch
(
UserException
e
)
{
_logger
.
error
(
e
.
getMessage
(),
e
);
}
}
else
{
_logger
.
warn
(
"Passwords didn't match for "
+
user
);
}
}
return
"redirect:/profile"
;
}
}
src/main/resources/content/language.properties
View file @
3211dc28
...
...
@@ -320,4 +320,7 @@ team.leave-team=Leave team
team.team-members
=
Team members
team.page.profile.title
=
Team: {0}
team.page.list.title
=
All teams