Merge branch 'dev' of lab.si.usi.ch:sa4-2020/the-sanmarinoes/backend into model-update

This commit is contained in:
Tommaso Rodolfo Masera 2020-02-28 15:29:01 +01:00
commit 2ae0ab5b15
23 changed files with 274 additions and 115 deletions

17
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,17 @@
code_quality:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
reports:
codequality: gl-code-quality-report.json

Binary file not shown.

View file

@ -1,7 +1,12 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtil;
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 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;
@ -14,10 +19,12 @@ import org.springframework.web.bind.annotation.*;
@RestController
@CrossOrigin
@RequestMapping("/auth")
public class JWTAuthenticationController {
public class AuthenticationController {
@Autowired private AuthenticationManager authenticationManager;
@Autowired private UserRepository userRepository;
@Autowired private JWTTokenUtil jwtTokenUtil;
@Autowired private JWTUserDetailsService userDetailsService;
@ -36,12 +43,24 @@ public class JWTAuthenticationController {
}
@PostMapping("/register")
public User register(@RequestBody User user) {
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());
if (u.getName() != null) oldUser.setName(u.getName());
if (u.getEmail() != null) {
oldUser.setEmail(u.getEmail());
// TODO: handle email verification
}
if (u.getPassword() != null) oldUser.setPassword(encoder.encode(u.getPassword()));
return userRepository.save(oldUser);
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(

View file

@ -3,6 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ButtonDimmer;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ButtonDimmerRepository;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -31,12 +32,12 @@ public class ButtonDimmerController {
}
@PostMapping
public ButtonDimmer save(@RequestBody ButtonDimmer bd) {
public ButtonDimmer save(@Valid @RequestBody ButtonDimmer bd) {
return buttonDimmerService.save(bd);
}
@PutMapping
public ButtonDimmer update(@RequestBody ButtonDimmer bd) {
public ButtonDimmer update(@Valid @RequestBody ButtonDimmer bd) {
return buttonDimmerService.save(bd);
}

View file

@ -2,7 +2,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLightRepository;
import java.util.List;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -22,27 +23,27 @@ public class DimmableLightController {
@Autowired private DimmableLightRepository dimmableLightService;
@GetMapping
public List<DimmableLight> getAll() {
return dimmableLightService.getList();
public Iterable<DimmableLight> findAll() {
return dimmableLightService.findAll();
}
@GetMapping("/{id}")
public DimmableLight getById(@PathVariable("id") long id) {
return dimmableLightService.getById();
public Optional<DimmableLight> findById(@PathVariable("id") long id) {
return dimmableLightService.findById(id);
}
@PostMapping
public DimmableLight create(@RequestBody DimmableLight dl) {
return dimmableLightService.create(dl);
public DimmableLight save(@Valid @RequestBody DimmableLight dl) {
return dimmableLightService.save(dl);
}
@PutMapping("/{id}")
public DimmableLight update(@PathVariable("id") long id, @RequestBody DimmableLight dl) {
return dimmableLightService.update(id, dl);
@PutMapping
public DimmableLight update(@Valid @RequestBody DimmableLight dl) {
return dimmableLightService.save(dl);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable("id") long id) {
dimmableLightService.delete(id);
dimmableLightService.deleteById(id);
}
}

View file

@ -3,6 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.KnobDimmer;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.KnobDimmerRepository;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -32,12 +33,12 @@ public class KnobDimmerController {
}
@PostMapping
public KnobDimmer save(@RequestBody KnobDimmer kd) {
public KnobDimmer save(@Valid @RequestBody KnobDimmer kd) {
return knobDimmerService.save(kd);
}
@PutMapping
public KnobDimmer update(@RequestBody KnobDimmer kd) {
public KnobDimmer update(@Valid @RequestBody KnobDimmer kd) {
return knobDimmerService.save(kd);
}

View file

@ -3,6 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensor;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -32,12 +33,12 @@ public class MotionSensorController {
}
@PostMapping
public MotionSensor save(@RequestBody MotionSensor ms) {
public MotionSensor save(@Valid @RequestBody MotionSensor ms) {
return motionSensorService.save(ms);
}
@PutMapping
public MotionSensor update(@RequestBody MotionSensor ms) {
public MotionSensor update(@Valid @RequestBody MotionSensor ms) {
return motionSensorService.save(ms);
}

View file

@ -3,6 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLight;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLightRepository;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -32,12 +33,12 @@ public class RegularLightController {
}
@PostMapping
public RegularLight save(@RequestBody RegularLight rl) {
public RegularLight save(@Valid @RequestBody RegularLight rl) {
return regularLightService.save(rl);
}
@PutMapping
public RegularLight update(@RequestBody RegularLight rl) {
public RegularLight update(@Valid @RequestBody RegularLight rl) {
return regularLightService.save(rl);
}

View file

@ -2,8 +2,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
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.*;
@RestController
@ -13,6 +16,8 @@ public class RoomController {
@Autowired private RoomRepository roomRepository;
@Autowired private UserRepository userRepository;
@GetMapping
public Iterable<Room> findAll() {
return roomRepository.findAll();
@ -24,12 +29,25 @@ public class RoomController {
}
@PostMapping
public Room save(@RequestBody Room r) {
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");
}
final String username = ((UserDetails) principal).getUsername();
final Long userId = userRepository.findByUsername(username).getId();
r.setUserId(userId);
r.setUser(null);
return roomRepository.save(r);
}
@PutMapping
public Room update(@RequestBody Room r) {
public Room update(@Valid @RequestBody Room r) {
return roomRepository.save(r);
}

View file

@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.*;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;
@ -24,12 +25,12 @@ public class SensorController {
}
@PostMapping
public Sensor save(@RequestBody Sensor s) {
public Sensor save(@Valid @RequestBody Sensor s) {
return sensorRepository.save(s);
}
@PutMapping
public Sensor update(@RequestBody Sensor s) {
public Sensor update(@Valid @RequestBody Sensor s) {
return sensorRepository.save(s);
}

View file

@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.*;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;
@ -24,12 +25,12 @@ public class SmartPlugController {
}
@PostMapping
public SmartPlug save(@RequestBody SmartPlug sp) {
public SmartPlug save(@Valid @RequestBody SmartPlug sp) {
return smartPlugRepository.save(sp);
}
@PutMapping
public SmartPlug update(@RequestBody SmartPlug sp) {
public SmartPlug update(@Valid @RequestBody SmartPlug sp) {
return smartPlugRepository.save(sp);
}

View file

@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.*;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;
@ -24,12 +25,12 @@ public class SwitchController {
}
@PostMapping
public Switch save(@RequestBody Switch s) {
public Switch save(@Valid @RequestBody Switch s) {
return switchRepository.save(s);
}
@PutMapping
public Switch update(@RequestBody Switch s) {
public Switch update(@Valid @RequestBody Switch s) {
return switchRepository.save(s);
}

View file

@ -1,40 +0,0 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
@RequestMapping("/user")
public class UserController {
@Autowired private UserRepository userRepository;
@GetMapping
public Iterable<User> findAll() {
return userRepository.findAll();
}
@GetMapping("/{id}")
public Optional<User> findById(@PathVariable("id") long id) {
return userRepository.findById(id);
}
@PostMapping
public User save(@RequestBody User u) {
return userRepository.save(u);
}
@PutMapping
public User update(@RequestBody User u) {
return userRepository.save(u);
}
@DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") long id) {
userRepository.deleteById(id);
}
}

View file

@ -1,17 +1,9 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
public class JWTRequest {
private String username;
private String password;
// need default constructor for JSON Parsing
public JWTRequest() {}
public JWTRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
public String getUsername() {
return this.username;
}

View file

@ -1,4 +1,4 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
public class JWTResponse {
private final String jwttoken;

View file

@ -0,0 +1,48 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
public class UserUpdateRequest {
/** The full name of the user */
@NotEmpty(message = "Please provide a full name")
private String name;
/** A non-salted password */
@NotEmpty(message = "Please provide a password")
private String password;
/**
* The user's email (validated according to criteria used in <code>&gt;input type="email"&lt;>
* </code>, technically not RFC 5322 compliant
*/
@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 String getPassword() {
return password;
}
public String getEmail() {
return email;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
}

View file

@ -1,6 +1,8 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import com.google.gson.annotations.SerializedName;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
/** Generic abstraction for a smart home device */
@Entity
@ -9,7 +11,10 @@ public abstract class Device {
/** Ways a device can behave in the automation flow. For now only input/output */
public enum FlowType {
@SerializedName("INPUT")
INPUT,
@SerializedName("OUTPUT")
OUTPUT
}
@ -21,11 +26,21 @@ public abstract class Device {
/** The room this device belongs in */
@ManyToOne
@JoinColumn(name = "room_id")
@JoinColumn(name = "room_id", nullable = false, updatable = false, insertable = false)
private Room room;
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
*/
@Column(name = "room_id", nullable = false)
@NotNull
private Long roomId;
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@Column private String name;
@NotNull
@Column(nullable = false)
private String name;
/**
* The name for the category of this particular device (e.g 'dimmer'). Not stored in the
@ -63,6 +78,14 @@ public abstract class Device {
this.name = name;
}
public Long getRoomId() {
return roomId;
}
public void setRoomId(Long roomId) {
this.roomId = roomId;
}
public Device(String kind, FlowType flowType) {
this.kind = kind;
this.flowType = flowType;

View file

@ -2,6 +2,9 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/** Represent a dimmable light */
@Entity
@ -12,13 +15,17 @@ public class DimmableLight extends Light {
}
/** The light intensity value. Goes from 0 (off) to 100 (on) */
@Column private int intensity = 0;
@NotNull
@Column(nullable = false)
@Min(1)
@Max(100)
private Integer intensity = 0;
public int getIntensity() {
public Integer getIntensity() {
return intensity;
}
public void setIntensity(int intensity) throws IllegalArgumentException {
public void setIntensity(Integer intensity) throws IllegalArgumentException {
if (intensity < 0 || intensity > 100) {
throw new IllegalArgumentException("The intensity level can't go below 0 or above 100");
}

View file

@ -4,6 +4,7 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.validation.constraints.NotNull;
/** Represents a generic light */
@Entity
@ -11,7 +12,8 @@ import javax.persistence.InheritanceType;
public abstract class Light extends OutputDevice {
/** Whether the light is on or not */
@Column(name = "light_on")
@Column(name = "light_on", nullable = false)
@NotNull
boolean on;
protected Light(String kind) {

View file

@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
/** Represents a room in the house owned by the user */
@Entity
@ -18,24 +19,36 @@ public class Room {
* https://www.baeldung.com/java-base64-image-string
* https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
*/
@Column(name = "icon", updatable = true, nullable = false)
@Lob
@Column(name = "icon", columnDefinition = "TEXT")
private byte[] icon;
@Column(name = "image", updatable = true, nullable = false)
@Lob
@Column(name = "image", columnDefinition = "TEXT")
private byte[] image;
/**
* User that owns the house this room is in as a foreign key id. To use when updating and
* inserting from a REST call.
*/
@NotNull
@Column(name = "user_id", nullable = false)
private Long userId;
/** The user given name of this room (e.g. 'Master bedroom') */
@NotNull
@Column(nullable = false)
private String name;
/** Collection of devices present in this room */
@OneToMany(mappedBy = "room")
private Set<Device> devices;
/** User that owns the house this room is in */
@ManyToOne
@JoinColumn(name = "user_id")
@JoinColumn(name = "user_id", nullable = false, updatable = false, insertable = false)
private User user;
/** The user given name of this room (e.g. 'Master bedroom') */
@Column private String name;
/** Collectiion of devices present in this room */
@OneToMany(mappedBy = "room")
private Set<Device> devices;
public Long getId() {
return id;
}
@ -44,6 +57,14 @@ public class Room {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public User getUser() {
return user;
}
@ -64,18 +85,24 @@ public class Room {
return devices;
}
public byte[] getIcon() {
return icon;
}
public void setIcon(byte[] icon) {
this.icon = icon;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
@Override
public String toString() {
return "Room{"
+ "id="
+ id
+ ", user="
+ user
+ ", name='"
+ name
+ '\''
+ ", devices="
+ devices
+ '}';
return "Room{" + "id=" + id + ", user=" + user + ", name='" + name + "\'}";
}
}

View file

@ -1,31 +1,45 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import com.google.gson.annotations.SerializedName;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.validation.constraints.NotNull;
/** A sensor input device that measures a quantity in a continuous scale (e.g. temperature) */
@Entity
public class Sensor extends InputDevice {
/** Type of sensor, i.e. of the thing the sensor measures. */
enum SensorType {
public enum SensorType {
/** A sensor that measures temperature in degrees celsius */
@SerializedName("TEMPERATURE")
TEMPERATURE,
/** A sensor that measures relative humidity in percentage points */
@SerializedName("HUMIDITY")
HUMIDITY,
/** A sensor that measures light in degrees */
@SerializedName("LIGHT")
LIGHT
}
/** The type of this sensor */
@Column
@Column(nullable = false)
@NotNull
@Enumerated(value = EnumType.STRING)
private SensorType sensor;
public SensorType getSensor() {
return sensor;
}
public void setSensor(SensorType sensor) {
this.sensor = sensor;
}
public Sensor() {
super("sensor");
}

View file

@ -2,13 +2,15 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
/** A smart plug that can be turned either on or off */
@Entity
public class SmartPlug extends OutputDevice {
/** Whether the smart plug is on */
@Column(name = "smart_plug_on")
@Column(name = "smart_plug_on", nullable = false)
@NotNull
private boolean on;
public boolean isOn() {

View file

@ -2,7 +2,11 @@ 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;
/** A user of the Smarthut application */
@Entity(name = "smarthutuser")
@ -14,16 +18,34 @@ public class User {
private Long id;
/** The full name of the user */
@Column @NotNull private String name;
@NotNull
@Column(nullable = false)
@NotEmpty(message = "Please provide a full name")
private String name;
/** The full name of the user */
@Column @NotNull private String username;
@NotNull
@Column(nullable = false)
@NotEmpty(message = "Please provide a username")
private String username;
/** A properly salted way to store the password TODO: define the implementation of salt */
@Column @NotNull private String password;
/** A properly salted way to store the password */
@NotNull
@Column(nullable = false)
@NotEmpty(message = "Please provide a password")
@Min(value = 6, message = "Your password should be at least 6 characters long")
private String password;
/** The user's email TODO: validate email in setters */
@Column @NotNull private String email;
/**
* The user's email (validated according to criteria used in <code>&gt;input type="email"&lt;>
* </code>, technically not RFC 5322 compliant
*/
@Column(nullable = false)
@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;
/** All rooms in the user's house */
@OneToMany(mappedBy = "user")