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/build.gradle b/build.gradle index ea7614f..d511c0b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ sourceCompatibility = "11" repositories { mavenCentral() + jcenter() } dependencies { @@ -17,9 +18,12 @@ 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' + 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 new file mode 100644 index 0000000..a51eb5a --- /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/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/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java index a8bcb1d..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,13 +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() - . // 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..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,22 +52,18 @@ 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; - } - + @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/controller/UserAccountController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java new file mode 100644 index 0000000..e238f16 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java @@ -0,0 +1,98 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse; +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; +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; + +@RestController +@EnableAutoConfiguration +@RequestMapping("/register") +public class UserAccountController { + + @Autowired private UserRepository userRepository; + + @Autowired private ConfirmationTokenRepository confirmationTokenRepository; + + @Autowired private EmailSenderService emailSenderService; + + @Autowired private BCryptPasswordEncoder encoder; + + @PostMapping + 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 (existingEmailUser != null || existingUsernameUser != null) { + throw new DuplicateRegistrationException(); + } else { + final User toSave = new User(); + // disable the user (it will be enabled on email confiration) + toSave.setEnabled(false); + + // 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(registrationData.getEmail()); + mailMessage.setSubject("Complete Registration!"); + mailMessage.setFrom("smarthut.sm@gmail.com"); + mailMessage.setText( + "To confirm your account, please click here : " + + "http://localhost:8080/register/confirm-account?token=" + + confirmationToken.getConfirmationToken()); + + emailSenderService.sendEmail(mailMessage); + + return new OkResponse(); + } + } + + @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); + // TODO: redirect to frontend + 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/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/DuplicateRegistrationException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateRegistrationException.java new file mode 100644 index 0000000..302c07e --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/DuplicateRegistrationException.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 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/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/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java index b2c907f..9ff5af2 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,9 +8,6 @@ import javax.persistence.OneToMany; */ @Entity public class ButtonDimmer extends Dimmer { - @OneToMany(mappedBy = "button_dimmer") - private Set lights = new HashSet(); - public ButtonDimmer() { super("button-dimmer"); } 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..f6c86a0 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java @@ -0,0 +1,74 @@ +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(); + } + + /** Constructor for hibernate reflective stuff things whatever */ + public ConfirmationToken() {} + + 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/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index 0d5d959..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; @@ -22,11 +23,13 @@ public abstract class Device { @Id @GeneratedValue(strategy = GenerationType.AUTO) @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/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index cbba6a2..d14e099 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,9 +8,6 @@ import javax.persistence.OneToMany; */ @Entity public class KnobDimmer extends Dimmer { - @OneToMany(mappedBy = "knob_dimmer") - private Set lights = new HashSet(); - public KnobDimmer() { super("knob-dimmer"); } 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 1be3df0..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,8 +1,13 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import io.swagger.annotations.ApiModelProperty; import java.util.Set; import javax.persistence.*; -import javax.validation.constraints.*; +import javax.validation.constraints.Email; +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") @@ -11,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 */ @@ -32,8 +38,7 @@ public class User { @Size( min = 6, max = 255, - message = - "Your password should be at least 6 characters long and at most 255 chars long") + message = "Your password should be at least 6 characters long and up to 255 chars long") private String password; /** @@ -49,8 +54,13 @@ 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() { return id; } @@ -95,6 +105,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 +121,17 @@ public class User { + ", name='" + name + '\'' + + ", username='" + + username + + '\'' + ", password='" + password + '\'' + ", email='" + email - + "\'}"; + + '\'' + + ", isEnabled=" + + isEnabled + + '}'; } } 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); } 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