From 990f1bd6d4bd3a8e585535388312aca43d04543d Mon Sep 17 00:00:00 2001 From: omenem Date: Thu, 27 Feb 2020 17:31:28 +0100 Subject: [PATCH 1/7] Initial Setup for email verification --- .../smarthut/Service/EmailSenderService.java | 23 ++++++ .../controller/UserAccountController.java | 68 ++++++++++++++++++ .../smarthut/models/ConfirmationToken.java | 71 +++++++++++++++++++ .../models/ConfirmationTokenRepository.java | 7 ++ .../smarthut/models/UserRepository.java | 2 + 5 files changed, 171 insertions(+) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java new file mode 100644 index 0000000..35bc509 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java @@ -0,0 +1,23 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.Service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +@Service("emailSenderService") +public class EmailSenderService { + + private JavaMailSender javaMailSender; + + @Autowired + public EmailSenderService(JavaMailSender javaMailSender) { + this.javaMailSender = javaMailSender; + } + + @Async + public void sendEmail(SimpleMailMessage email) { + javaMailSender.send(email); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java new file mode 100644 index 0000000..6b16039 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java @@ -0,0 +1,68 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.Service.EmailSenderService; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; + +@RestController +@EnableAutoConfiguration +@RequestMapping("/register") +public class UserAccountController { + + @Autowired private UserRepository userRepository; + + @Autowired private ConfirmationTokenRepository confirmationTokenRepository; + + @Autowired private EmailSenderService emailSenderService; + + @GetMapping + public ModelAndView displayRegistration(ModelAndView modelAndView, User user) { + modelAndView.addObject("user", user); + modelAndView.setViewName("register"); + return modelAndView; + } + + @PostMapping + public ModelAndView registerUser(ModelAndView modelAndView, User user) { + + User existingUser = userRepository.findByEmailIgnoreCase(user.getEmail()); + // Check if an User with the same email already exists + if (existingUser != null) { + modelAndView.addObject("message", "This email already exists!"); + modelAndView.setViewName("error"); + } else { + userRepository.save(user); + + ConfirmationToken confirmationToken = new ConfirmationToken(user); + + confirmationTokenRepository.save(confirmationToken); + + SimpleMailMessage mailMessage = new SimpleMailMessage(); + mailMessage.setTo(user.getEmail()); + mailMessage.setSubject("Complete Registration!"); + mailMessage.setFrom("smarthut.sm@gmail.com"); + mailMessage.setText( + "To confirm your account, please click here : " + + "http://localhost:8082/confirm-account?token=" + + confirmationToken.getConfirmationToken()); + + emailSenderService.sendEmail(mailMessage); + + modelAndView.addObject("emailId", user.getEmail()); + + modelAndView.setViewName("successfulRegisteration"); + } + + return modelAndView; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java new file mode 100644 index 0000000..7f0b71c --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java @@ -0,0 +1,71 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +import java.util.Date; +import java.util.UUID; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +public class ConfirmationToken { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id", updatable = false, nullable = false) + private Long id; + + @Column(name = "confirmation_token") + private String confirmationToken; + + @Temporal(TemporalType.TIMESTAMP) + private Date createdDate; + + @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER) + @JoinColumn(nullable = false, name = "user_id") + private User user; + + public ConfirmationToken(User user) { + this.user = user; + createdDate = new Date(); + confirmationToken = UUID.randomUUID().toString(); + } + + public Long getId() { + return id; + } + + public String getConfirmationToken() { + return confirmationToken; + } + + public Date getCreatedDate() { + return createdDate; + } + + public User getUser() { + return user; + } + + public void setId(Long id) { + this.id = id; + } + + public void setConfirmationToken(String confirmationToken) { + this.confirmationToken = confirmationToken; + } + + public void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + + public void setUser(User user) { + this.user = user; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java new file mode 100644 index 0000000..4bc18ce --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java @@ -0,0 +1,7 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +import org.springframework.data.repository.CrudRepository; + +public interface ConfirmationTokenRepository extends CrudRepository { + ConfirmationToken findByConfirmationToken(String confirmationToken); +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/UserRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/UserRepository.java index 0b8c62a..01fd897 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/UserRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/UserRepository.java @@ -5,4 +5,6 @@ import org.springframework.data.repository.CrudRepository; public interface UserRepository extends CrudRepository { User findByUsername(String username); + + User findByEmailIgnoreCase(String email); } From 2271f4a5b92b2143f673908276cbe42ad5ac8034 Mon Sep 17 00:00:00 2001 From: omenem Date: Thu, 27 Feb 2020 21:39:38 +0100 Subject: [PATCH 2/7] Email Verifcation Works --- build.gradle | 1 + .../smarthut/config/WebSecurityConfig.java | 4 ++ .../controller/AuthenticationController.java | 7 --- .../controller/UserAccountController.java | 43 +++++++++++++++---- .../sanmarinoes/smarthut/dto/OkResponse.java | 6 +++ .../DuplicateEmailRegistrationException.java | 11 +++++ .../error/EmailTokenNotFoundException.java | 11 +++++ .../smarthut/models/ConfirmationToken.java | 3 ++ .../sa4/sanmarinoes/smarthut/models/User.java | 26 +++++++++-- src/main/resources/application.properties | 14 +++++- 10 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/EmailTokenNotFoundException.java diff --git a/build.gradle b/build.gradle index ea7614f..7c7384d 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'org.springframework.security:spring-security-web' implementation 'org.postgresql:postgresql' diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java index a8bcb1d..9bbea89 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java @@ -54,6 +54,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .permitAll() .antMatchers("/auth/register") .permitAll() + .antMatchers("/register") + .permitAll() + .antMatchers("/register/confirm-account") + .permitAll() . // all other requests need to be authenticated anyRequest() diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index ed82692..4646bd1 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -42,13 +42,6 @@ public class AuthenticationController { return new JWTResponse(token); } - @PostMapping("/register") - public User register(@Valid @RequestBody User user) { - user.setPassword(encoder.encode(user.getPassword())); - users.save(user); - return user; - } - @PatchMapping("/update") public User update(@Valid @RequestBody final UserUpdateRequest u, final Principal principal) { final User oldUser = userRepository.findByUsername(principal.getName()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java index 6b16039..b0210e0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java @@ -1,16 +1,24 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; import ch.usi.inf.sa4.sanmarinoes.smarthut.Service.EmailSenderService; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateEmailRegistrationException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.EmailTokenNotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.mail.SimpleMailMessage; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @@ -25,6 +33,8 @@ public class UserAccountController { @Autowired private EmailSenderService emailSenderService; + @Autowired private BCryptPasswordEncoder encoder; + @GetMapping public ModelAndView displayRegistration(ModelAndView modelAndView, User user) { modelAndView.addObject("user", user); @@ -33,14 +43,17 @@ public class UserAccountController { } @PostMapping - public ModelAndView registerUser(ModelAndView modelAndView, User user) { - + public OkResponse registerUser(@Valid @RequestBody User user) + throws DuplicateEmailRegistrationException { + System.out.println(user); User existingUser = userRepository.findByEmailIgnoreCase(user.getEmail()); + // Check if an User with the same email already exists if (existingUser != null) { - modelAndView.addObject("message", "This email already exists!"); - modelAndView.setViewName("error"); + throw new DuplicateEmailRegistrationException(); } else { + // encode user's password + user.setPassword(encoder.encode(user.getPassword())); userRepository.save(user); ConfirmationToken confirmationToken = new ConfirmationToken(user); @@ -53,16 +66,28 @@ public class UserAccountController { mailMessage.setFrom("smarthut.sm@gmail.com"); mailMessage.setText( "To confirm your account, please click here : " - + "http://localhost:8082/confirm-account?token=" + + "http://localhost:8080/register/confirm-account?token=" + confirmationToken.getConfirmationToken()); emailSenderService.sendEmail(mailMessage); - modelAndView.addObject("emailId", user.getEmail()); - - modelAndView.setViewName("successfulRegisteration"); + return new OkResponse(); } + } - return modelAndView; + @GetMapping(value = "/confirm-account") + public OkResponse confirmUserAccount(@RequestParam("token") @NotNull String confirmationToken) + throws EmailTokenNotFoundException { + final ConfirmationToken token = + confirmationTokenRepository.findByConfirmationToken(confirmationToken); + + if (token != null) { + final User user = userRepository.findByEmailIgnoreCase(token.getUser().getEmail()); + user.setEnabled(true); + userRepository.save(user); + return new OkResponse(); + } else { + throw new EmailTokenNotFoundException(); + } } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java new file mode 100644 index 0000000..e3de94e --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java @@ -0,0 +1,6 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +/** A dummy DTO to return when there is no data to return */ +public class OkResponse { + private boolean success = true; +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java new file mode 100644 index 0000000..3c37b2b --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java @@ -0,0 +1,11 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.error; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.BAD_REQUEST) +public class DuplicateEmailRegistrationException extends Exception { + public DuplicateEmailRegistrationException() { + super("Email already belonging to another user"); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/EmailTokenNotFoundException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/EmailTokenNotFoundException.java new file mode 100644 index 0000000..b3e38b1 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/EmailTokenNotFoundException.java @@ -0,0 +1,11 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.error; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.BAD_REQUEST) +public class EmailTokenNotFoundException extends Exception { + public EmailTokenNotFoundException() { + super("Email verification token not found in DB"); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java index 7f0b71c..f6c86a0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java @@ -37,6 +37,9 @@ public class ConfirmationToken { confirmationToken = UUID.randomUUID().toString(); } + /** Constructor for hibernate reflective stuff things whatever */ + public ConfirmationToken() {} + public Long getId() { return id; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java index e4ce24e..6c1ff6b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java @@ -3,10 +3,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import java.util.Set; import javax.persistence.*; import javax.validation.constraints.Email; -import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; /** A user of the Smarthut application */ @Entity(name = "smarthutuser") @@ -33,7 +33,10 @@ public class User { @NotNull @Column(nullable = false) @NotEmpty(message = "Please provide a password") - @Min(value = 6, message = "Your password should be at least 6 characters long") + @Size( + min = 6, + max = 255, + message = "Your password should be at least 6 characters long and up to 255 chars long") private String password; /** @@ -51,6 +54,9 @@ public class User { @OneToMany(mappedBy = "user") private Set rooms; + @Column(nullable = false) + private Boolean isEnabled = false; + public Long getId() { return id; } @@ -95,6 +101,14 @@ public class User { return rooms; } + public Boolean getEnabled() { + return isEnabled; + } + + public void setEnabled(Boolean enabled) { + isEnabled = enabled; + } + @Override public String toString() { return "User{" @@ -103,11 +117,17 @@ public class User { + ", name='" + name + '\'' + + ", username='" + + username + + '\'' + ", password='" + password + '\'' + ", email='" + email - + "\'}"; + + '\'' + + ", isEnabled=" + + isEnabled + + '}'; } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 30d24cd..9bfe2a7 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -10,4 +10,16 @@ spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl spring.jpa.properties.hibernate.format_sql=true -jwt.secret=thiskeymustbeverylongorthethingcomplainssoiamjustgoingtowritehereabunchofgarbageciaomamma \ No newline at end of file +jwt.secret=thiskeymustbeverylongorthethingcomplainssoiamjustgoingtowritehereabunchofgarbageciaomamma + +spring.mail.test-connection=true +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.username=smarthut.sm@gmail.com +spring.mail.password=dcadvbagqfkwbfts +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.connectiontimeout=5000 +spring.mail.properties.mail.smtp.timeout=5000 +spring.mail.properties.mail.smtp.writetimeout=5000 \ No newline at end of file From 6be9c80c6ea8a5cbe591d43ba3a9589d68c65052 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 1 Mar 2020 12:15:43 +0100 Subject: [PATCH 3/7] Work on Swagger integration --- build.gradle | 3 ++ .../smarthut/config/GsonConfig.java | 36 +++++++++++++++++++ .../smarthut/config/SpringFoxConfig.java | 25 +++++++++++++ .../smarthut/config/WebSecurityConfig.java | 14 +++++--- .../controller/AuthenticationController.java | 24 +++++++++---- .../sanmarinoes/smarthut/models/Device.java | 11 ++++-- .../sa4/sanmarinoes/smarthut/models/Room.java | 4 +++ .../sa4/sanmarinoes/smarthut/models/User.java | 3 ++ 8 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java diff --git a/build.gradle b/build.gradle index ea7614f..728859b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ sourceCompatibility = "11" repositories { mavenCentral() + jcenter() } dependencies { @@ -20,6 +21,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'org.springframework.security:spring-security-web' implementation 'org.postgresql:postgresql' + compile "io.springfox:springfox-swagger2:2.9.2" + compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2' implementation('org.springframework.boot:spring-boot-starter-web') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json' diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java new file mode 100644 index 0000000..3a2ab37 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java @@ -0,0 +1,36 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import com.google.gson.*; +import java.lang.reflect.Type; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.GsonHttpMessageConverter; +import springfox.documentation.spring.web.json.Json; + +/** + * Spring configuration in order to register the GSON type adapter needed to avoid serializing twice + * Springfox Swagger JSON output (see: https://stackoverflow.com/a/30220562) + */ +@Configuration +public class GsonConfig { + @Bean + public GsonHttpMessageConverter gsonHttpMessageConverter() { + GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); + converter.setGson(gson()); + return converter; + } + + private Gson gson() { + final GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); + return builder.create(); + } +} + +/** GSON type adapter needed to avoid serializing twice Springfox Swagger JSON output */ +class SpringfoxJsonToGsonAdapter implements JsonSerializer { + @Override + public JsonElement serialize(Json json, Type type, JsonSerializationContext context) { + return JsonParser.parseString(json.value()); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java new file mode 100644 index 0000000..ad88e75 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java @@ -0,0 +1,25 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.AuthenticationController; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +@ComponentScan(basePackageClasses = {AuthenticationController.class}) +public class SpringFoxConfig { + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build(); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java index a8bcb1d..bb548f0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java @@ -50,13 +50,17 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .disable() // dont authenticate this particular request .authorizeRequests() - .antMatchers("/auth/login") + .antMatchers( + "/auth/login", + "/auth/register", + "/swagger-ui.html", + "/v2/api-docs", + "/webjars/**", + "/swagger-resources/**", + "/csrf") .permitAll() - .antMatchers("/auth/register") - .permitAll() - . // all other requests need to be authenticated - anyRequest() + .anyRequest() .authenticated() .and() . diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index ed82692..0ba000e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -7,7 +7,6 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserUpdateRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import java.security.Principal; import javax.validation.Valid; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; @@ -21,18 +20,31 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/auth") public class AuthenticationController { - @Autowired private AuthenticationManager authenticationManager; + private final AuthenticationManager authenticationManager; - @Autowired private UserRepository userRepository; + private final UserRepository userRepository; - @Autowired private JWTTokenUtil jwtTokenUtil; + private final JWTTokenUtil jwtTokenUtil; - @Autowired private JWTUserDetailsService userDetailsService; + private final JWTUserDetailsService userDetailsService; - @Autowired private UserRepository users; + private final UserRepository users; private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + public AuthenticationController( + AuthenticationManager authenticationManager, + UserRepository userRepository, + JWTTokenUtil jwtTokenUtil, + JWTUserDetailsService userDetailsService, + UserRepository users) { + this.authenticationManager = authenticationManager; + this.userRepository = userRepository; + this.jwtTokenUtil = jwtTokenUtil; + this.userDetailsService = userDetailsService; + this.users = users; + } + @PostMapping("/login") public JWTResponse login(@RequestBody JWTRequest authenticationRequest) throws Exception { authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index 834104d..63ce4d2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -1,6 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -22,11 +23,13 @@ public abstract class Device { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) + @ApiModelProperty(hidden = true) private long id; /** The room this device belongs in */ @ManyToOne @JoinColumn(name = "room_id", nullable = false, updatable = false, insertable = false) + @ApiModelProperty(hidden = true) private Room room; /** @@ -46,13 +49,17 @@ public abstract class Device { * The name for the category of this particular device (e.g 'dimmer'). Not stored in the * database but set thanks to constructors */ - @Transient private final String kind; + @ApiModelProperty(hidden = true) + @Transient + private final String kind; /** * The way this device behaves in the automation flow. Not stored in the database but set thanks * to constructors */ - @Transient private final FlowType flowType; + @ApiModelProperty(hidden = true) + @Transient + private final FlowType flowType; public long getId() { return id; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index c0fae3f..4a82ab2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import io.swagger.annotations.ApiModelProperty; import java.util.Set; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -11,6 +12,7 @@ public class Room { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) + @ApiModelProperty(hidden = true) private Long id; /** @@ -41,12 +43,14 @@ public class Room { private String name; /** Collection of devices present in this room */ + @ApiModelProperty(hidden = true) @OneToMany(mappedBy = "room") private Set devices; /** User that owns the house this room is in */ @ManyToOne @JoinColumn(name = "user_id", nullable = false, updatable = false, insertable = false) + @ApiModelProperty(hidden = true) private User user; public Long getId() { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java index 7909fe8..8c6798b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import io.swagger.annotations.ApiModelProperty; import java.util.Set; import javax.persistence.*; import javax.validation.constraints.*; @@ -11,6 +12,7 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) + @ApiModelProperty(hidden = true) private Long id; /** The full name of the user */ @@ -49,6 +51,7 @@ public class User { /** All rooms in the user's house */ @OneToMany(mappedBy = "user") + @ApiModelProperty(hidden = true) private Set rooms; public Long getId() { From b294144a2289ff0af58e7975f574543550461218 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 1 Mar 2020 13:46:15 +0100 Subject: [PATCH 4/7] Idea configuration removed so not to cause complaining for the SDK different installations between team members --- .gitignore | 2 +- .idea/.name | 1 - .idea/jarRepositories.xml | 20 -------------------- .idea/vcs.xml | 6 ------ 4 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 .idea/.name delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 1375525..1b1a367 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.idea/misc.xml +.idea/** **/.DS_Store diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 03128f2..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -smarthut \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index fdc392f..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 6ce90e61cb7b108c16e240e1d867bb9ecd93bee6 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 1 Mar 2020 15:13:37 +0100 Subject: [PATCH 5/7] Swagger configuration --- .../smarthut/config/SpringFoxConfig.java | 60 +++++++++++++++++-- .../controller/AuthenticationController.java | 14 +++-- .../smarthut/models/ButtonDimmer.java | 43 ------------- .../smarthut/models/KnobDimmer.java | 50 ---------------- 4 files changed, 65 insertions(+), 102 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java index ad88e75..743d119 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java @@ -1,25 +1,77 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.config; -import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.AuthenticationController; +import static springfox.documentation.builders.PathSelectors.regex; + +import java.util.List; +import java.util.function.Predicate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.SecurityScheme; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; +/** + * This class configures the automated REST documentation tool Swagger for this project. The + * documentation can be seen by going to http://localhost:8080/swaggeer-ui.html + */ @Configuration @EnableSwagger2 -@ComponentScan(basePackageClasses = {AuthenticationController.class}) +@ComponentScan("ch.usi.inf.sa4.sanmarinoes.smarthut") public class SpringFoxConfig { + + /** + * Main definition of Springfox / swagger configuration + * + * @return a Docket object containing the swagger configuration + */ @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) - .paths(PathSelectors.any()) + .paths(paths()::test) + .build() + .apiInfo(apiInfo()) + .securitySchemes(securitySchemes()); + } + + /** + * Configures the documentation about the smarthut authentication system + * + * @return a list of springfox authentication configurations + */ + private static List securitySchemes() { + return List.of(new ApiKey("Bearer", "Authorization", "header")); + } + + /** + * Configures the paths the documentation must be generated for. Add a path here only when the + * spec has been totally defined. + * + * @return A predicate that tests whether a path must be included or not + */ + private Predicate paths() { + return regex("/auth.*")::apply; + } + + /** + * Returns the metadata about the smarthut project + * + * @return metadata about smarthut + */ + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("SmartHut.sm API") + .description("Backend API for the SanMariones version of the SA4 SmartHut project") + .termsOfServiceUrl("https://www.youtube.com/watch?v=9KxTcDsy9Gs") + .license("WTFPL") + .version("dev branch") .build(); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index 0ba000e..e4c575e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -5,6 +5,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserUpdateRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import io.swagger.annotations.Authorization; import java.security.Principal; import javax.validation.Valid; import org.springframework.security.authentication.AuthenticationManager; @@ -61,15 +62,18 @@ public class AuthenticationController { return user; } + @Authorization(value = "Bearer") @PatchMapping("/update") - public User update(@Valid @RequestBody final UserUpdateRequest u, final Principal principal) { + public User update( + @Valid @RequestBody final UserUpdateRequest userData, final Principal principal) { final User oldUser = userRepository.findByUsername(principal.getName()); - if (u.getName() != null) oldUser.setName(u.getName()); - if (u.getEmail() != null) { - oldUser.setEmail(u.getEmail()); + if (userData.getName() != null) oldUser.setName(userData.getName()); + if (userData.getEmail() != null) { + oldUser.setEmail(userData.getEmail()); // TODO: handle email verification } - if (u.getPassword() != null) oldUser.setPassword(encoder.encode(u.getPassword())); + if (userData.getPassword() != null) + oldUser.setPassword(encoder.encode(userData.getPassword())); return userRepository.save(oldUser); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java index 23a69f8..e666c3e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java @@ -1,9 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -import java.util.HashSet; -import java.util.Set; import javax.persistence.Entity; -import javax.persistence.OneToMany; /** * Represents a dimmer that can only instruct an increase or decrease of intensity (i.e. like a @@ -11,47 +8,7 @@ import javax.persistence.OneToMany; */ @Entity public class ButtonDimmer extends Dimmer { - @OneToMany(mappedBy = "button_dimmer") - private Set lights = new HashSet(); - public ButtonDimmer() { super("button-dimmer"); } - - /** Increases the current intensity level of the dimmable light by 1 */ - public void increaseIntensity() { - for (DimmableLight dl : lights) { - dl.setIntensity(dl.getIntensity() + 1); - } - } - - /** Decreases the current intensity level of the dimmable light by 1 */ - public void decreaseIntensity() { - for (DimmableLight dl : lights) { - dl.setIntensity(dl.getIntensity() - 1); - } - } - - /** - * Adds a DimmableLight to this set of DimmableLights - * - * @param dl The DimmableLight to be added - */ - public void addLight(DimmableLight dl) { - lights.add(dl); - } - - /** - * Removes the given DimmableLight - * - * @param dl The DimmableLight to be removed - */ - public void removeLight(DimmableLight dl) { - lights.remove(dl); - } - - /** Clears this set */ - public void clearSet() { - lights.clear(); - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index 5c5c603..80d59ab 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java @@ -1,9 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -import java.util.HashSet; -import java.util.Set; import javax.persistence.Entity; -import javax.persistence.OneToMany; /** * Represents a dimmer able to set absolute intensity values (i.e. knowing the absolute intensity @@ -11,54 +8,7 @@ import javax.persistence.OneToMany; */ @Entity public class KnobDimmer extends Dimmer { - @OneToMany(mappedBy = "knob_dimmer") - private Set lights = new HashSet(); - public KnobDimmer() { super("knob-dimmer"); } - - /** - * Increases or decreases the current intensity level by 5, moving between absolute multiples of - * 5 between 0 and 100, of all dimmable lights mapped to this knob - * - * @param inc The direction the knob is turned with - */ - public void modifyIntensity(boolean inc) { - - for (DimmableLight dl : lights) { - int remainder = dl.getIntensity() / 5; - - if (inc) { - dl.setIntensity(dl.getIntensity() - remainder); - dl.setIntensity((dl.getIntensity() + 5) % 105); - } else { - dl.setIntensity(dl.getIntensity() + (5 - remainder)); - dl.setIntensity((dl.getIntensity() - 5) % 105); - } - } - } - - /** - * Adds a DimmableLight to this set of DimmableLights - * - * @param dl The DimmableLight to be added - */ - public void addLight(DimmableLight dl) { - lights.add(dl); - } - - /** - * Removes the given DimmableLight - * - * @param dl The DimmableLight to be removed - */ - public void removeLight(DimmableLight dl) { - lights.remove(dl); - } - - /** Clears this set */ - public void clearSet() { - lights.clear(); - } } From 51e6bb1f90a69fba603a8c3b9eeabade60b28e00 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Mon, 2 Mar 2020 16:56:43 +0100 Subject: [PATCH 6/7] Merge --- .../smarthut/config/WebSecurityConfig.java | 20 +++++----- .../controller/AuthenticationController.java | 37 +++++++++++++------ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java index 9bbea89..e38d0df 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java @@ -50,17 +50,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .disable() // dont authenticate this particular request .authorizeRequests() - .antMatchers("/auth/login") + .antMatchers( + "/auth/login", + "/auth/register", + "/swagger-ui.html", + "/register", + "/register/confirm-account", + "/v2/api-docs", + "/webjars/**", + "/swagger-resources/**", + "/csrf") .permitAll() - .antMatchers("/auth/register") - .permitAll() - .antMatchers("/register") - .permitAll() - .antMatchers("/register/confirm-account") - .permitAll() - . // all other requests need to be authenticated - anyRequest() + .anyRequest() .authenticated() .and() . diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index 4646bd1..cc9ed2e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -5,9 +5,9 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserUpdateRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import io.swagger.annotations.Authorization; import java.security.Principal; import javax.validation.Valid; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; @@ -21,18 +21,28 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/auth") public class AuthenticationController { - @Autowired private AuthenticationManager authenticationManager; + private final AuthenticationManager authenticationManager; - @Autowired private UserRepository userRepository; + private final UserRepository userRepository; - @Autowired private JWTTokenUtil jwtTokenUtil; + private final JWTTokenUtil jwtTokenUtil; - @Autowired private JWTUserDetailsService userDetailsService; - - @Autowired private UserRepository users; + private final JWTUserDetailsService userDetailsService; private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + public AuthenticationController( + AuthenticationManager authenticationManager, + UserRepository userRepository, + JWTTokenUtil jwtTokenUtil, + JWTUserDetailsService userDetailsService, + UserRepository users) { + this.authenticationManager = authenticationManager; + this.userRepository = userRepository; + this.jwtTokenUtil = jwtTokenUtil; + this.userDetailsService = userDetailsService; + } + @PostMapping("/login") public JWTResponse login(@RequestBody JWTRequest authenticationRequest) throws Exception { authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); @@ -42,15 +52,18 @@ public class AuthenticationController { return new JWTResponse(token); } + @Authorization(value = "Bearer") @PatchMapping("/update") - public User update(@Valid @RequestBody final UserUpdateRequest u, final Principal principal) { + public User update( + @Valid @RequestBody final UserUpdateRequest userData, final Principal principal) { final User oldUser = userRepository.findByUsername(principal.getName()); - if (u.getName() != null) oldUser.setName(u.getName()); - if (u.getEmail() != null) { - oldUser.setEmail(u.getEmail()); + if (userData.getName() != null) oldUser.setName(userData.getName()); + if (userData.getEmail() != null) { + oldUser.setEmail(userData.getEmail()); // TODO: handle email verification } - if (u.getPassword() != null) oldUser.setPassword(encoder.encode(u.getPassword())); + if (userData.getPassword() != null) + oldUser.setPassword(encoder.encode(userData.getPassword())); return userRepository.save(oldUser); } From fcf7189e44db1ec6d30263bb9bb5afff2ecd5b45 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Mon, 2 Mar 2020 17:11:29 +0100 Subject: [PATCH 7/7] Updated email verification workflow to current code practices --- build.gradle | 3 + .../smarthut/Service/EmailSenderService.java | 2 +- .../smarthut/config/GsonConfig.java | 36 +++++++++ .../smarthut/config/SpringFoxConfig.java | 77 +++++++++++++++++++ .../smarthut/controller/RoomController.java | 40 ++++++---- .../controller/UserAccountController.java | 47 ++++++----- .../smarthut/dto/RoomSaveRequest.java | 43 +++++++++++ .../smarthut/dto/UserRegistrationRequest.java | 67 ++++++++++++++++ ...va => DuplicateRegistrationException.java} | 6 +- .../sanmarinoes/smarthut/models/Device.java | 13 +++- .../sa4/sanmarinoes/smarthut/models/Room.java | 4 + .../sanmarinoes/smarthut/models/Switch.java | 24 ++++++ .../sa4/sanmarinoes/smarthut/models/User.java | 6 +- 13 files changed, 325 insertions(+), 43 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserRegistrationRequest.java rename src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/{DuplicateEmailRegistrationException.java => DuplicateRegistrationException.java} (54%) diff --git a/build.gradle b/build.gradle index 7c7384d..d511c0b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ sourceCompatibility = "11" repositories { mavenCentral() + jcenter() } dependencies { @@ -21,6 +22,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'org.springframework.security:spring-security-web' implementation 'org.postgresql:postgresql' + compile "io.springfox:springfox-swagger2:2.9.2" + compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2' implementation('org.springframework.boot:spring-boot-starter-web') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json' diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java index 35bc509..a51eb5a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java @@ -1,4 +1,4 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.Service; +package ch.usi.inf.sa4.sanmarinoes.smarthut.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.SimpleMailMessage; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java new file mode 100644 index 0000000..3a2ab37 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java @@ -0,0 +1,36 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import com.google.gson.*; +import java.lang.reflect.Type; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.GsonHttpMessageConverter; +import springfox.documentation.spring.web.json.Json; + +/** + * Spring configuration in order to register the GSON type adapter needed to avoid serializing twice + * Springfox Swagger JSON output (see: https://stackoverflow.com/a/30220562) + */ +@Configuration +public class GsonConfig { + @Bean + public GsonHttpMessageConverter gsonHttpMessageConverter() { + GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); + converter.setGson(gson()); + return converter; + } + + private Gson gson() { + final GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); + return builder.create(); + } +} + +/** GSON type adapter needed to avoid serializing twice Springfox Swagger JSON output */ +class SpringfoxJsonToGsonAdapter implements JsonSerializer { + @Override + public JsonElement serialize(Json json, Type type, JsonSerializationContext context) { + return JsonParser.parseString(json.value()); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java new file mode 100644 index 0000000..b5e8d10 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java @@ -0,0 +1,77 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import static springfox.documentation.builders.PathSelectors.regex; + +import java.util.List; +import java.util.function.Predicate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * This class configures the automated REST documentation tool Swagger for this project. The + * documentation can be seen by going to http://localhost:8080/swaggeer-ui.html + */ +@Configuration +@EnableSwagger2 +@ComponentScan("ch.usi.inf.sa4.sanmarinoes.smarthut") +public class SpringFoxConfig { + + /** + * Main definition of Springfox / swagger configuration + * + * @return a Docket object containing the swagger configuration + */ + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(paths()::test) + .build() + .apiInfo(apiInfo()) + .securitySchemes(securitySchemes()); + } + + /** + * Configures the documentation about the smarthut authentication system + * + * @return a list of springfox authentication configurations + */ + private static List securitySchemes() { + return List.of(new ApiKey("Bearer", "Authorization", "header")); + } + + /** + * Configures the paths the documentation must be generated for. Add a path here only when the + * spec has been totally defined. + * + * @return A predicate that tests whether a path must be included or not + */ + private Predicate paths() { + return ((Predicate) regex("/auth.*")::apply).or(regex("/register.*")::apply); + } + + /** + * Returns the metadata about the smarthut project + * + * @return metadata about smarthut + */ + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("SmartHut.sm API") + .description("Backend API for the SanMariones version of the SA4 SmartHut project") + .termsOfServiceUrl("https://www.youtube.com/watch?v=9KxTcDsy9Gs") + .license("WTFPL") + .version("dev branch") + .build(); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java index 9ff6e2e..a25e4b5 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java @@ -1,11 +1,12 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RoomSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import java.security.Principal; import java.util.*; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.*; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @@ -28,27 +29,38 @@ public class RoomController { return roomRepository.findById(id); } - @PostMapping - public Room save(@Valid @RequestBody Room r) { - final Object principal = - SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - - if (!(principal instanceof UserDetails)) { - throw new IllegalStateException("User is not logged in"); - } + private Room save(final RoomSaveRequest r, final Principal principal, boolean setWhenNull) { + Room newRoom = new Room(); final String username = ((UserDetails) principal).getUsername(); final Long userId = userRepository.findByUsername(username).getId(); + final String img = r.getImage(); + final String icon = r.getIcon(); - r.setUserId(userId); - r.setUser(null); + newRoom.setUserId(userId); + newRoom.setUser(null); + if (img != null) { + newRoom.setImage(img.getBytes()); + } else if (setWhenNull) { + newRoom.setImage(null); + } + if (icon != null) { + newRoom.setIcon(icon.getBytes()); + } else if (setWhenNull) { + newRoom.setIcon(null); + } - return roomRepository.save(r); + return roomRepository.save(newRoom); + } + + @PostMapping + public Room create(@Valid @RequestBody RoomSaveRequest r, final Principal principal) { + return this.save(r, principal, true); } @PutMapping - public Room update(@Valid @RequestBody Room r) { - return roomRepository.save(r); + public Room update(@Valid @RequestBody RoomSaveRequest r, final Principal principal) { + return this.save(r, principal, false); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java index b0210e0..e238f16 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java @@ -1,13 +1,14 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import ch.usi.inf.sa4.sanmarinoes.smarthut.Service.EmailSenderService; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse; -import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateEmailRegistrationException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.EmailTokenNotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.EmailSenderService; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.springframework.beans.factory.annotation.Autowired; @@ -20,7 +21,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.ModelAndView; @RestController @EnableAutoConfiguration @@ -35,33 +35,37 @@ public class UserAccountController { @Autowired private BCryptPasswordEncoder encoder; - @GetMapping - public ModelAndView displayRegistration(ModelAndView modelAndView, User user) { - modelAndView.addObject("user", user); - modelAndView.setViewName("register"); - return modelAndView; - } - @PostMapping - public OkResponse registerUser(@Valid @RequestBody User user) - throws DuplicateEmailRegistrationException { - System.out.println(user); - User existingUser = userRepository.findByEmailIgnoreCase(user.getEmail()); + public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData) + throws DuplicateRegistrationException { + final User existingEmailUser = + userRepository.findByEmailIgnoreCase(registrationData.getEmail()); + final User existingUsernameUser = + userRepository.findByUsername(registrationData.getUsername()); // Check if an User with the same email already exists - if (existingUser != null) { - throw new DuplicateEmailRegistrationException(); + if (existingEmailUser != null || existingUsernameUser != null) { + throw new DuplicateRegistrationException(); } else { - // encode user's password - user.setPassword(encoder.encode(user.getPassword())); - userRepository.save(user); + final User toSave = new User(); + // disable the user (it will be enabled on email confiration) + toSave.setEnabled(false); - ConfirmationToken confirmationToken = new ConfirmationToken(user); + // encode user's password + toSave.setPassword(encoder.encode(registrationData.getPassword())); + + // set other fields + toSave.setName(registrationData.getName()); + toSave.setUsername(registrationData.getUsername()); + toSave.setEmail(registrationData.getEmail()); + userRepository.save(toSave); + + ConfirmationToken confirmationToken = new ConfirmationToken(toSave); confirmationTokenRepository.save(confirmationToken); SimpleMailMessage mailMessage = new SimpleMailMessage(); - mailMessage.setTo(user.getEmail()); + mailMessage.setTo(registrationData.getEmail()); mailMessage.setSubject("Complete Registration!"); mailMessage.setFrom("smarthut.sm@gmail.com"); mailMessage.setText( @@ -85,6 +89,7 @@ public class UserAccountController { final User user = userRepository.findByEmailIgnoreCase(token.getUser().getEmail()); user.setEnabled(true); userRepository.save(user); + // TODO: redirect to frontend return new OkResponse(); } else { throw new EmailTokenNotFoundException(); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java new file mode 100644 index 0000000..f813b1c --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java @@ -0,0 +1,43 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.persistence.Lob; +import javax.validation.constraints.NotNull; + +public class RoomSaveRequest { + /** + * Icon and image are to be given as byte[]. In order to get an encoded string from it, the + * Base64.getEncoder().encodeToString(byte[] content) should be used. For further information: + * https://www.baeldung.com/java-base64-image-string + * https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html + */ + @Lob private String icon; + + @Lob private String image; + + /** The user given name of this room (e.g. 'Master bedroom') */ + @NotNull private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserRegistrationRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserRegistrationRequest.java new file mode 100644 index 0000000..b41720a --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserRegistrationRequest.java @@ -0,0 +1,67 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.validation.constraints.*; + +public class UserRegistrationRequest { + + /** The full name of the user */ + @NotNull + @NotEmpty(message = "Please provide a full name") + private String name; + + /** The full name of the user */ + @NotNull + @NotEmpty(message = "Please provide a username") + private String username; + + /** A properly salted way to store the password */ + @NotNull + @NotEmpty(message = "Please provide a password") + @Size( + min = 6, + max = 255, + message = "Your password should be at least 6 characters long and up to 255 chars long") + private String password; + + /** + * The user's email (validated according to criteria used in >input type="email"<> + * , technically not RFC 5322 compliant + */ + @NotNull + @NotEmpty(message = "Please provide an email") + @Email(message = "Please provide a valid email address") + @Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address") + private String email; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateRegistrationException.java similarity index 54% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateRegistrationException.java index 3c37b2b..302c07e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateEmailRegistrationException.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateRegistrationException.java @@ -4,8 +4,8 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(code = HttpStatus.BAD_REQUEST) -public class DuplicateEmailRegistrationException extends Exception { - public DuplicateEmailRegistrationException() { - super("Email already belonging to another user"); +public class DuplicateRegistrationException extends Exception { + public DuplicateRegistrationException() { + super("Email or username already belonging to another user"); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index 834104d..871b67c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -1,6 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -21,12 +22,14 @@ public abstract class Device { /** Device identifier */ @Id @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "id", updatable = false, nullable = false) + @Column(name = "id", updatable = false, nullable = false, unique = true) + @ApiModelProperty(hidden = true) private long id; /** The room this device belongs in */ @ManyToOne @JoinColumn(name = "room_id", nullable = false, updatable = false, insertable = false) + @ApiModelProperty(hidden = true) private Room room; /** @@ -46,13 +49,17 @@ public abstract class Device { * The name for the category of this particular device (e.g 'dimmer'). Not stored in the * database but set thanks to constructors */ - @Transient private final String kind; + @ApiModelProperty(hidden = true) + @Transient + private final String kind; /** * The way this device behaves in the automation flow. Not stored in the database but set thanks * to constructors */ - @Transient private final FlowType flowType; + @ApiModelProperty(hidden = true) + @Transient + private final FlowType flowType; public long getId() { return id; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index c0fae3f..4a82ab2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import io.swagger.annotations.ApiModelProperty; import java.util.Set; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -11,6 +12,7 @@ public class Room { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) + @ApiModelProperty(hidden = true) private Long id; /** @@ -41,12 +43,14 @@ public class Room { private String name; /** Collection of devices present in this room */ + @ApiModelProperty(hidden = true) @OneToMany(mappedBy = "room") private Set devices; /** User that owns the house this room is in */ @ManyToOne @JoinColumn(name = "user_id", nullable = false, updatable = false, insertable = false) + @ApiModelProperty(hidden = true) private User user; public Long getId() { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java index fbf1eed..6f0eb99 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java @@ -1,11 +1,35 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import javax.persistence.Column; import javax.persistence.Entity; /** A switch input device TODO: define switch behaviour (push button vs on/off state) */ @Entity public class Switch extends InputDevice { + + /** The state of this switch */ + @Column(nullable = false, name = "switch_on") + private boolean on; + public Switch() { super("switch"); } + + /** + * Setter method for this Switch + * + * @param state The state to be set + */ + void setState(boolean state) { + on = state; + } + + /** + * Getter method for this Switch + * + * @return This Switch on state + */ + boolean getState() { + return on; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java index 6c1ff6b..96b20d8 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import io.swagger.annotations.ApiModelProperty; import java.util.Set; import javax.persistence.*; import javax.validation.constraints.Email; @@ -15,6 +16,7 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) + @ApiModelProperty(hidden = true) private Long id; /** The full name of the user */ @@ -43,7 +45,7 @@ public class User { * The user's email (validated according to criteria used in >input type="email"<> * , technically not RFC 5322 compliant */ - @Column(nullable = false) + @Column(nullable = false, unique = true) @NotNull @NotEmpty(message = "Please provide an email") @Email(message = "Please provide a valid email address") @@ -52,9 +54,11 @@ public class User { /** All rooms in the user's house */ @OneToMany(mappedBy = "user") + @ApiModelProperty(hidden = true) private Set rooms; @Column(nullable = false) + @ApiModelProperty(hidden = true) private Boolean isEnabled = false; public Long getId() {