From 6987adad9da482cbc73018ce613bf837fe270b2d Mon Sep 17 00:00:00 2001 From: trifonnt Date: Tue, 12 Apr 2016 23:09:53 +0300 Subject: [PATCH] Added: - new model class "Role" - new JPA Repository "RoleRepository" - Eclipse launch configuration for starting as Spring application - application-DEV.template.properties which template for Developer properties file. Modified Spring Security configuration to read User roles from database. --- README.DEV.md | 41 +++++++ jprime - Application.launch | 23 ++++ pom.xml | 2 +- src/main/java/site/app/SecurityConfig.java | 10 +- .../controller/AdminArticleController.java | 23 ++-- src/main/java/site/facade/AdminService.java | 33 +++++- src/main/java/site/model/Role.java | 49 ++++++++ src/main/java/site/model/User.java | 110 +++++++++++------- .../java/site/repository/RoleRepository.java | 18 +++ .../site/repository/SessionRepository.java | 1 - src/main/resources/.gitignore | 1 + .../application-DEV.template.properties | 60 ++++++++++ 12 files changed, 311 insertions(+), 60 deletions(-) create mode 100644 README.DEV.md create mode 100644 jprime - Application.launch create mode 100644 src/main/java/site/model/Role.java create mode 100644 src/main/java/site/repository/RoleRepository.java create mode 100644 src/main/resources/.gitignore create mode 100644 src/main/resources/application-DEV.template.properties diff --git a/README.DEV.md b/README.DEV.md new file mode 100644 index 00000000..2047be19 --- /dev/null +++ b/README.DEV.md @@ -0,0 +1,41 @@ + +## When starting jPrime for first time. + + +### Create new database in MySQL DB + +$ mysql -u root -p + +mysql> USE mysql; + +mysql> CREATE DATABASE IF NOT EXISTS javabg + DEFAULT CHARACTER SET = utf8 + DEFAULT COLLATE = utf8_unicode_ci; + +mysql> GRANT ALL ON javabg.* TO 'root'@'localhost' IDENTIFIED BY 'admin'; + +mysql> exit; + + +### Configure REAL Mail or FAKE Mail server + +##### FAKE mail servers used only during development + + - www.mailtrap.io + + +### Copy application-DEV.template.properties to application-DEV.properties + +$ cd jprime/src/main/resources/ + +$ cp application-DEV.template.properties application-DEV.properties + + +### Adjust DEV profile properties file(application-DEV.properties) if needed + +$ vim /jprime/src/main/resources/application-DEV.properties + +spring.boot.admin.url=http://localhost:9090 + +server.port=9090 + diff --git a/jprime - Application.launch b/jprime - Application.launch new file mode 100644 index 00000000..89b18a41 --- /dev/null +++ b/jprime - Application.launch @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 22efb4cf..1b6af089 100644 --- a/pom.xml +++ b/pom.xml @@ -200,7 +200,7 @@ - site.app.Application + site.app.Application 4.1.5.RELEASE UTF-8 1.5.0.M1 diff --git a/src/main/java/site/app/SecurityConfig.java b/src/main/java/site/app/SecurityConfig.java index c02a1d1c..40b9fd01 100644 --- a/src/main/java/site/app/SecurityConfig.java +++ b/src/main/java/site/app/SecurityConfig.java @@ -22,6 +22,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import site.model.Role; import site.model.User; import site.repository.UserRepository; @@ -62,8 +63,8 @@ public Authentication authenticate(Authentication authentication) throws Authent String providedPassword = (String) authentication.getCredentials(); //admin hack - if(email.equals("admin") && providedPassword.equals(environment.getProperty("admin.password"))){ - return new UsernamePasswordAuthenticationToken(email, providedPassword, Collections.singleton(new SimpleGrantedAuthority("ADMIN"))); + if(email.equals("admin") && providedPassword.equals(environment.getProperty("admin.password"))) { + return new UsernamePasswordAuthenticationToken(email, providedPassword, Collections.singleton(new SimpleGrantedAuthority(Role.ADMIN_NAME))); } User user = userRepository.findUserByEmail(email); @@ -71,8 +72,9 @@ public Authentication authenticate(Authentication authentication) throws Authent if (user == null || !passwordEncoder().matches(providedPassword, user.getPassword())) { throw new BadCredentialsException("Username/Password does not match for " + authentication.getPrincipal()); } - - return new UsernamePasswordAuthenticationToken(email, providedPassword, Collections.singleton(new SimpleGrantedAuthority("USER"))); + // @Trifon - Read User roles from DB + // return new UsernamePasswordAuthenticationToken(email, providedPassword, Collections.singleton(new SimpleGrantedAuthority("USER"))); + return new UsernamePasswordAuthenticationToken(email, providedPassword, user.getRoles()); } @Override diff --git a/src/main/java/site/controller/AdminArticleController.java b/src/main/java/site/controller/AdminArticleController.java index b1577f0f..49778efc 100644 --- a/src/main/java/site/controller/AdminArticleController.java +++ b/src/main/java/site/controller/AdminArticleController.java @@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import site.facade.AdminService; import site.model.Article; +import site.model.Role; import site.model.User; import javax.validation.Valid; @@ -45,17 +46,18 @@ public String add(@Valid final Article article, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "/admin/article/edit.jsp"; } - User admin = this.adminFacade.findUserByEmail("admin@jsprime.io"); - if (admin == null) { - admin = new User(); - admin.setEmail("admin@jsprime.io"); - admin.setFirstName("Admin"); - admin.setLastName(""); - this.adminFacade.saveUser(admin); + User adminUser = this.adminFacade.findUserByEmail(User.ADMIN_EMAIL); + Role adminRole = this.adminFacade.findRoleByNameOrCreate(Role.ADMIN_NAME); //@Trifon + if (adminUser == null) { + adminUser = this.adminFacade.createRegularUser(User.ADMIN_EMAIL); + adminUser.setFirstName("Admin"); + adminUser.setLastName(""); + adminUser.addRole( adminRole ); //@Trifon + adminUser = this.adminFacade.saveUser(adminUser); //refresh - admin = this.adminFacade.findUserByEmail("admin@jsprime.io"); + adminUser = this.adminFacade.findUserByEmail(User.ADMIN_EMAIL); // TODO - Maybe this refresh is not needed as admin is already updated by saveUser()? } - article.setAuthor(admin); + article.setAuthor(adminUser); this.adminFacade.saveArticle(article); return "redirect:/admin/article/view"; @@ -83,5 +85,4 @@ public String remove(@PathVariable("itemId") Long itemId, Model model) { adminFacade.deleteArticle(itemId); return "redirect:/admin/article/view"; } - -} +} \ No newline at end of file diff --git a/src/main/java/site/facade/AdminService.java b/src/main/java/site/facade/AdminService.java index c9a23ce2..9eeeb324 100644 --- a/src/main/java/site/facade/AdminService.java +++ b/src/main/java/site/facade/AdminService.java @@ -40,6 +40,10 @@ public class AdminService { @Qualifier(UserRepository.NAME) private UserRepository userRepository; + @Autowired + @Qualifier(RoleRepository.NAME) + private RoleRepository roleRepository; + @Autowired @Qualifier(TagRepository.NAME) private TagRepository tagRepository; @@ -149,9 +153,36 @@ public User findOneUser(Long id){ return userRepository.findOne(id); } + //@Trifon + public Role findRoleByName(String roleName) { + return roleRepository.findByName( roleName ); + } + //@Trifon + public Role findRoleByNameOrCreate(String roleName) { + Role role = this.findRoleByName( roleName ); + if (role == null) { + role = new Role( roleName ); + role = this.saveRole( role ); + } + return role; + } + //@Trifon + public Role saveRole(Role role) { + return roleRepository.save( role ); + } + //@Trifon + public User createRegularUser(String email) { + Role userRole = this.findRoleByNameOrCreate(Role.USER_NAME); + + User user = new User(); + user.setEmail( email ); + user.addRole( userRole ); + return user; + } + //currently used for admin TODO:to be fixed with normal authentication with spring public User findUserByEmail(String email){ - if(userRepository.findByEmail(email).size()>0) { + if (userRepository.findByEmail(email).size()>0) { return userRepository.findByEmail(email).get(0); } return null; diff --git a/src/main/java/site/model/Role.java b/src/main/java/site/model/Role.java new file mode 100644 index 00000000..a683ed8a --- /dev/null +++ b/src/main/java/site/model/Role.java @@ -0,0 +1,49 @@ +package site.model; + +import javax.persistence.Entity; + +import org.springframework.security.core.GrantedAuthority; + +/** + * @author Trifon Trifonov + */ +@Entity +public class Role + extends AbstractEntity + implements GrantedAuthority +{ + + private static final long serialVersionUID = 1L; + + public final static String USER_NAME = "USER"; + public final static String ADMIN_NAME = "ADMIN"; + + private String name; + + public Role() { + + } + public Role(String name) { + this.name = name; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + @Override + public String getAuthority() { + return name; + } + + @Override + public String toString() { + return "Role ["+ + "id=" + getId() + + ", name=" + name + + "]"; + } +} \ No newline at end of file diff --git a/src/main/java/site/model/User.java b/src/main/java/site/model/User.java index c93d27c3..babdb05a 100644 --- a/src/main/java/site/model/User.java +++ b/src/main/java/site/model/User.java @@ -6,40 +6,51 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; +import javax.persistence.JoinTable; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Transient; import org.hibernate.validator.constraints.Email; -import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; -import org.hibernate.validator.constraints.NotEmpty; @Entity public class User extends AbstractEntity { /** - * Default serial version uid. - */ - private static final long serialVersionUID = 1L; - + * Default serial version uid. + */ + private static final long serialVersionUID = 1L; + + public static final String ADMIN_EMAIL = "admin@jsprime.io"; + private String firstName; - + private String lastName; - + @Column(unique = true) - @NotBlank - @Email + @NotBlank + @Email private String email; - + private String password; - + @Transient private String cpassword; - + private String phone; - @OneToMany(mappedBy = "author", fetch = FetchType.LAZY, targetEntity = Article.class) - private Collection
articles = new HashSet<>(); - + @OneToMany(mappedBy = "author", fetch = FetchType.LAZY, targetEntity = Article.class) + private Collection
articles = new HashSet<>(); + + //@Trifon + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "user_role" + , joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id") + , inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id") ) + private Collection roles = new HashSet<>(); + + public String getFirstName() { return firstName; } @@ -80,32 +91,32 @@ public void setPhone(String phone) { this.phone = phone; } - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof User)) - return false; - - User user = (User) o; - - if (email != null && !email.equals(user.email)) - return false; - if (firstName != null && !firstName.equals(user.firstName)) - return false; - if (lastName != null && !lastName.equals(user.lastName)) - return false; - - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof User)) + return false; + + User user = (User) o; + + if (email != null && !email.equals(user.email)) + return false; + if (firstName != null && !firstName.equals(user.firstName)) + return false; + if (lastName != null && !lastName.equals(user.lastName)) + return false; + + return true; + } - @Override - public int hashCode() { - int result = firstName.hashCode(); - result = 31 * result + lastName.hashCode(); - result = 31 * result + email.hashCode(); - return result; - } + @Override + public int hashCode() { + int result = firstName.hashCode(); + result = 31 * result + lastName.hashCode(); + result = 31 * result + email.hashCode(); + return result; + } public String getPassword() { return password; @@ -119,6 +130,21 @@ public String getCpassword() { return cpassword; } + public Collection getRoles() { + return roles; + } + + public void setRoles(Collection roles) { + this.roles = roles; + } + + public void addRole(Role role) { + if (role == null) { + throw new IllegalArgumentException("Role MUST not be null!"); + } + roles.add(role); + } + @Override public String toString() { return "User [firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]"; @@ -127,4 +153,4 @@ public String toString() { public void setCpassword(String cpassword) { this.cpassword = cpassword; } -} +} \ No newline at end of file diff --git a/src/main/java/site/repository/RoleRepository.java b/src/main/java/site/repository/RoleRepository.java new file mode 100644 index 00000000..293e0a3c --- /dev/null +++ b/src/main/java/site/repository/RoleRepository.java @@ -0,0 +1,18 @@ +package site.repository; + +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + +import site.model.Role; + + +/** + * @author Trifon Trifonov + */ +@Repository(value = RoleRepository.NAME) +public interface RoleRepository extends PagingAndSortingRepository { + + String NAME = "roleRepository"; + + public Role findByName(String name); +} \ No newline at end of file diff --git a/src/main/java/site/repository/SessionRepository.java b/src/main/java/site/repository/SessionRepository.java index 3daec999..d0efadab 100644 --- a/src/main/java/site/repository/SessionRepository.java +++ b/src/main/java/site/repository/SessionRepository.java @@ -2,7 +2,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; -import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.stereotype.Repository; import site.model.Session; diff --git a/src/main/resources/.gitignore b/src/main/resources/.gitignore new file mode 100644 index 00000000..f982fd56 --- /dev/null +++ b/src/main/resources/.gitignore @@ -0,0 +1 @@ +/application-DEV.properties diff --git a/src/main/resources/application-DEV.template.properties b/src/main/resources/application-DEV.template.properties new file mode 100644 index 00000000..c272c544 --- /dev/null +++ b/src/main/resources/application-DEV.template.properties @@ -0,0 +1,60 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/javabg?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8_general_ci&characterSetResults=utf8&autoDeserialize=true&useConfigs=maxPerformance +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + +spring.jpa.open-in-view=true +spring.jpa.show-sql=false +spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect +spring.jpa.generate-ddl=true +spring.jpa.hibernate.ddl-auto=none +spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.DefaultNamingStrategy +spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE +spring.jpa.properties.hibernate.hikari.dataSource.cachePrepStmts=true +spring.jpa.properties.hibernate.hikari.dataSource.prepStmtCacheSize=250 +spring.jpa.properties.hibernate.hikari.dataSource.prepStmtCacheSqlLimit=2048 +spring.jpa.properties.hibernate.hikari.dataSource.useServerPrepStmts=true +spring.jpa.properties.hibernate.generate_statistics=false +spring.jpa.properties.hibernate.cache.use_structured_entries=true +spring.jpa.properties.hibernate.archive.autodetection=class +spring.jpa.properties.hibernate.show_sql=false +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.use_sql_comments=true +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect +spring.jpa.properties.hibernate.jdbc.batch_size=50 +spring.jpa.properties.hibernate.order_update=true +spring.jpa.properties.hibernate.connection.characterEncoding=utf-8 +spring.jpa.properties.hibernate.connection.CharSet=utf-8 +spring.jpa.properties.hibernate.connection.useUnicode=true +spring.jpa.properties.hibernate.connection.autocommit=true +spring.jpa.properties.hibernate.cache.use_second_level_cache=true +spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory +spring.jpa.properties.hibernate.cache.region_prefix= +spring.jpa.properties.hibernate.cache.use_query_cache=true +spring.jpa.properties.hibernate.cache.use_minimal_puts=true +# JTA +spring.jta.enabled=true + +spring.mail.host = mail.jprime.io +spring.mail.username = conference@jprime.io +#spring.mail.password = pass it on the command line as system property + +spring.mail.properties.mail.smtp.auth = true +spring.mail.properties.mail.smtp.socketFactory.port = 465 +spring.mail.properties.mail.smtp.socketFactory.class = javax.net.ssl.SSLSocketFactory +spring.mail.properties.mail.smtp.socketFactory.fallback = false + +admin.password = password +spring.boot.admin.url=http://localhost:8080 + +#mihail.stoynov: different binding address +#management.port=80 +#management.address=127.0.0.1 +#server.address=127.0.0.1 #don't enable it, many issues with iptables, firewall instead + +site.url=jprime.io +site.url.reset.password=https://jprime.io/createNewPassword?tokenId= + +site.reset.password.token.duration.hours=2 + +