From 1ab4fee6ff2fc60b1ddbf506e6af41816294adf3 Mon Sep 17 00:00:00 2001 From: omenem Date: Wed, 26 Feb 2020 18:56:47 +0100 Subject: [PATCH 1/7] Little changes to Room and dimmableLight classes --- .../controller/DimmableLightController.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java index 0cd1144..32faf36 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java @@ -2,7 +2,7 @@ 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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.web.bind.annotation.DeleteMapping; @@ -22,27 +22,27 @@ public class DimmableLightController { @Autowired private DimmableLightRepository dimmableLightService; @GetMapping - public List getAll() { - return dimmableLightService.getList(); + public Iterable findAll() { + return dimmableLightService.findAll(); } @GetMapping("/{id}") - public DimmableLight getById(@PathVariable("id") long id) { - return dimmableLightService.getById(); + public Optional findById(@PathVariable("id") long id) { + return dimmableLightService.findById(id); } @PostMapping - public DimmableLight create(@RequestBody DimmableLight dl) { - return dimmableLightService.create(dl); + public DimmableLight save(@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(@RequestBody DimmableLight dl) { + return dimmableLightService.save(dl); } @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { - dimmableLightService.delete(id); + dimmableLightService.deleteById(id); } } From b2353e98347f72a561167977e484a422f3e4d994 Mon Sep 17 00:00:00 2001 From: omenem Date: Wed, 26 Feb 2020 18:58:01 +0100 Subject: [PATCH 2/7] Small Changes --- .../smarthut/controller/RoomController.java | 17 +++++++++ .../sanmarinoes/smarthut/models/Device.java | 9 ++++- .../sa4/sanmarinoes/smarthut/models/Room.java | 35 +++++++++++-------- 3 files changed, 45 insertions(+), 16 deletions(-) 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 c60d45b..7d23922 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 @@ -4,6 +4,8 @@ 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.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @RestController @@ -13,6 +15,8 @@ public class RoomController { @Autowired private RoomRepository roomRepository; + @Autowired private UserRepository userRepository; + @GetMapping public Iterable findAll() { return roomRepository.findAll(); @@ -25,6 +29,19 @@ public class RoomController { @PostMapping public Room save(@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); } 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 d6108e8..3ec45b9 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 @@ -21,9 +21,16 @@ 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") + private Long roomId; + /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ @Column private String name; 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 6f36557..f297927 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 @@ -18,21 +18,28 @@ 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) + @Column(name = "icon", nullable = false) private byte[] icon; - @Column(name = "image", updatable = true, nullable = false) + @Column(name = "image", nullable = false) private byte[] image; /** 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; + /** + * User that owns the house this room is in as a foreign key id. To use when updating and + * inserting from a REST call. + */ + @Column(name = "user_id") + private Long userId; + /** The user given name of this room (e.g. 'Master bedroom') */ @Column private String name; - /** Collectiion of devices present in this room */ + /** Collection of devices present in this room */ @OneToMany(mappedBy = "room") private Set devices; @@ -44,6 +51,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; } @@ -66,16 +81,6 @@ public class Room { @Override public String toString() { - return "Room{" - + "id=" - + id - + ", user=" - + user - + ", name='" - + name - + '\'' - + ", devices=" - + devices - + '}'; + return "Room{" + "id=" + id + ", user=" + user + ", name='" + name + "\'}"; } } From 48441dfe07e2da191b0e62f785f45b95dd54029f Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Wed, 26 Feb 2020 21:41:54 +0100 Subject: [PATCH 3/7] Started work on input validation --- .idea/misc.xml | 2 +- ...ler.java => AuthenticationController.java} | 20 +++++++- .../controller/ButtonDimmerController.java | 5 +- .../controller/DimmableLightController.java | 5 +- .../controller/KnobDimmerController.java | 5 +- .../controller/MotionSensorController.java | 5 +- .../controller/RegularLightController.java | 5 +- .../smarthut/controller/RoomController.java | 5 +- .../smarthut/controller/SensorController.java | 5 +- .../controller/SmartPlugController.java | 5 +- .../smarthut/controller/SwitchController.java | 5 +- .../smarthut/controller/UserController.java | 40 --------------- .../smarthut/{models => dto}/JWTRequest.java | 10 +--- .../smarthut/{models => dto}/JWTResponse.java | 2 +- .../smarthut/dto/UserUpdateRequest.java | 51 +++++++++++++++++++ .../sanmarinoes/smarthut/models/Device.java | 20 +++++++- .../smarthut/models/DimmableLight.java | 13 +++-- .../sanmarinoes/smarthut/models/Light.java | 4 +- .../sa4/sanmarinoes/smarthut/models/Room.java | 40 +++++++++++---- .../sanmarinoes/smarthut/models/Sensor.java | 18 ++++++- .../smarthut/models/SmartPlug.java | 4 +- .../sa4/sanmarinoes/smarthut/models/User.java | 32 +++++++++--- 22 files changed, 206 insertions(+), 95 deletions(-) rename src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/{JWTAuthenticationController.java => AuthenticationController.java} (73%) delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserController.java rename src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/{models => dto}/JWTRequest.java (60%) rename src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/{models => dto}/JWTResponse.java (80%) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 7c6191b..241f098 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/JWTAuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java similarity index 73% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/JWTAuthenticationController.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index 6e3d01b..f7dee78 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/JWTAuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -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,21 @@ 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; } + @PutMapping("/update") + public User update(@Valid @RequestBody final UserUpdateRequest u, final Principal principal) { + final User oldUser = userRepository.findByUsername(principal.getName()); + oldUser.setName(u.getName()); + oldUser.setEmail(u.getEmail()); + oldUser.setPassword(encoder.encode(u.getPassword())); + return userRepository.save(oldUser); + } + private void authenticate(String username, String password) throws Exception { try { authenticationManager.authenticate( diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java index 1098824..7c2d0b7 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java @@ -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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java index 32faf36..5391e43 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java @@ -3,6 +3,7 @@ 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.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 DimmableLightController { } @PostMapping - public DimmableLight save(@RequestBody DimmableLight dl) { + public DimmableLight save(@Valid @RequestBody DimmableLight dl) { return dimmableLightService.save(dl); } @PutMapping - public DimmableLight update(@RequestBody DimmableLight dl) { + public DimmableLight update(@Valid @RequestBody DimmableLight dl) { return dimmableLightService.save(dl); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java index 0696202..3dd53d7 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java @@ -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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java index 5ac2612..15c964f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java @@ -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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java index 9a97abb..2ec1453 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java @@ -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); } 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 7d23922..9ff6e2e 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 @@ -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.security.core.context.SecurityContextHolder; @@ -28,7 +29,7 @@ public class RoomController { } @PostMapping - public Room save(@RequestBody Room r) { + public Room save(@Valid @RequestBody Room r) { final Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); @@ -46,7 +47,7 @@ public class RoomController { } @PutMapping - public Room update(@RequestBody Room r) { + public Room update(@Valid @RequestBody Room r) { return roomRepository.save(r); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index 04fe6fb..957328e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java index a6304b4..a0aee71 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java @@ -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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java index 4cceb34..7eb83a0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java @@ -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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserController.java deleted file mode 100644 index 941cf9b..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserController.java +++ /dev/null @@ -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 findAll() { - return userRepository.findAll(); - } - - @GetMapping("/{id}") - public Optional 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); - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/JWTRequest.java similarity index 60% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTRequest.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/JWTRequest.java index 66d0c75..d750a50 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/JWTRequest.java @@ -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; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTResponse.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/JWTResponse.java similarity index 80% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTResponse.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/JWTResponse.java index 9eb1092..7bc04f2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTResponse.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/JWTResponse.java @@ -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; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java new file mode 100644 index 0000000..5000bb1 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java @@ -0,0 +1,51 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +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 */ + @NotNull + @NotEmpty(message = "Please provide a password") + 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") + @Pattern( + message = "Please provide a valid email", + regexp = "/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/") + 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; + } +} 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 3ec45b9..834104d 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,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 } @@ -28,11 +33,14 @@ public abstract class Device { * 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") + @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 @@ -70,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; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java index 666fbee..6a35248 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java @@ -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"); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java index bf1ed39..68a819b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java @@ -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) { 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 f297927..c0fae3f 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 @@ -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,31 +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", nullable = false) + @Lob + @Column(name = "icon", columnDefinition = "TEXT") private byte[] icon; - @Column(name = "image", nullable = false) + @Lob + @Column(name = "image", columnDefinition = "TEXT") private byte[] image; - /** User that owns the house this room is in */ - @ManyToOne - @JoinColumn(name = "user_id", nullable = false, updatable = false, insertable = false) - private User user; - /** * User that owns the house this room is in as a foreign key id. To use when updating and * inserting from a REST call. */ - @Column(name = "user_id") + @NotNull + @Column(name = "user_id", nullable = false) private Long userId; /** The user given name of this room (e.g. 'Master bedroom') */ - @Column private String name; + @NotNull + @Column(nullable = false) + private String name; /** Collection of devices present in this room */ @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) + private User user; + public Long getId() { return id; } @@ -79,6 +85,22 @@ 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 + "\'}"; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java index e5d7938..e3fbae0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java @@ -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"); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java index e7fd36a..e0d7981 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java @@ -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() { 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 7f9ec95..a8f239c 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 @@ -2,7 +2,9 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import java.util.Set; import javax.persistence.*; +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 +16,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") + 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 >input type="email"<> + * , technically not RFC 5322 compliant + */ + @Column(nullable = false) + @NotNull + @NotEmpty(message = "Please provide an email") + @Pattern( + message = "Please provide a valid email", + regexp = "^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$") + private String email; /** All rooms in the user's house */ @OneToMany(mappedBy = "user") From 40785d0cf1f00c3055f9e4c77ba92fe198cf91e1 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Thu, 27 Feb 2020 09:33:03 +0100 Subject: [PATCH 4/7] Better email validation for user and partial update for /auth/update --- .../smarthut/controller/AuthenticationController.java | 11 +++++++---- .../sanmarinoes/smarthut/dto/UserUpdateRequest.java | 9 +++------ .../usi/inf/sa4/sanmarinoes/smarthut/models/User.java | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) 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 f7dee78..ed82692 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 @@ -49,12 +49,15 @@ public class AuthenticationController { return user; } - @PutMapping("/update") + @PatchMapping("/update") public User update(@Valid @RequestBody final UserUpdateRequest u, final Principal principal) { final User oldUser = userRepository.findByUsername(principal.getName()); - oldUser.setName(u.getName()); - oldUser.setEmail(u.getEmail()); - oldUser.setPassword(encoder.encode(u.getPassword())); + 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); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java index 5000bb1..551b84a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserUpdateRequest.java @@ -1,7 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; +import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; public class UserUpdateRequest { @@ -10,7 +10,6 @@ public class UserUpdateRequest { private String name; /** A non-salted password */ - @NotNull @NotEmpty(message = "Please provide a password") private String password; @@ -18,11 +17,9 @@ public class UserUpdateRequest { * 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") - @Pattern( - message = "Please provide a valid email", - regexp = "/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/") + @Email(message = "Please provide a valid email address") + @Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address") private String email; public String getName() { 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 a8f239c..62d2cd7 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 @@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import java.util.Set; import javax.persistence.*; +import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; @@ -40,9 +41,8 @@ public class User { @Column(nullable = false) @NotNull @NotEmpty(message = "Please provide an email") - @Pattern( - message = "Please provide a valid email", - regexp = "^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$") + @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 */ From f70084dd4004911844fef66c6fcf425d147b6168 Mon Sep 17 00:00:00 2001 From: omenem Date: Thu, 27 Feb 2020 09:40:44 +0100 Subject: [PATCH 5/7] Added minimum length of password --- .idea/misc.xml | 2 +- .../java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 241f098..7c6191b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file 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 62d2cd7..e4ce24e 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,6 +3,7 @@ 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; @@ -32,6 +33,7 @@ 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") private String password; /** From 3c7749b2718c62a3cae0cc13099384d9a97aa4f7 Mon Sep 17 00:00:00 2001 From: tommi27 Date: Thu, 27 Feb 2020 15:48:53 +0100 Subject: [PATCH 6/7] polished er diagram for current db model --- docs/er.pdf | Bin 17531 -> 20743 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/er.pdf b/docs/er.pdf index 70efc63850e47b581396041ab990fdfd9e12a784..9735d6a95ca787b327a915741adece8bb146461d 100644 GIT binary patch delta 20286 zcmV)CK*GQKhyjP90gxgDG&VCZlb=8+f6ZJ;a~(+$zWZ17(I(2V#*jT2m?aopMSfe;0_> z9^}Nk9bQO$pI%iplw!3K&#Bc|(od{z5vwTw_MdMamQnXA;H0HlgSN}bNxdpbLcaO% z=BK}qc9C+8)~e;h-}p-2WO#SJalj7Don|}zy5yf+<|L$Zr1hGlx^wM};_qecl zpA3=AWkd*$R*V+sHGbSH2Ojo~-i;>eMEKR9Qaf}PqY#7S`xud1#-p%G8$0DDHCro$ z*PW;+ey#12I)VAB5h*B8Cm^6oBDZVp`ui#Yk2S3-t?Aw5Y?qfqqpN=y8PHfpMYgv7 z!pK09pewr)8Tu$Pe=1w$N|Eu$cgnxL{_^EV`v24CZO51%np#HD{?yfPF@jng3K&T~lkfagE;EPJr1h`2roi(V%%0 z#_A@f5lS_0zn>~clX2R->BL^u^l8MVauYQyDn~_=wc9x?e|nfzsd2;$;{n~B44)c@ z^he=PxC&E>hHoFPKizLaU}T&kpr~85?e2#Gf{C#%5~&tN3}MqhI&_AqwAW5^*xiX7 zc}EBraRlC-cDLo+SlOWzM8I-jm?$p=W)gNm)L0c(c?Owfw#tJPH7WZ5Y=}s&@hp8;1s~9meA!wHQ^< z=#B8SfTMvhCxDZZRQ?)*>Lfyds1w#YPSq0V+)>GI(in;5I-_E!b4vK7i*suA+G9fQ z?`20<+FtWsU)nSZRjM!;|bF@TkUk+Dq{SUwCjaD#}3&WP0>M5roNA!x}FJIpav6SlSb7-(q2e^YaCTSS$%v)TzHo~bEN zf5n(rf*3Rg8eCoiF@*Ry1G}CzpLz3lGv^tC+GSY5Zf3V}&29IkmO`r`#G!zwe%5qLtXeFq2sLaa6 z1mHBPrB#%7&=nnjpiz}Zfi+%gPDYz`29977IjO$;Cha^oa>O8}Xyd6%v;a)PAtBkz z8;uG^*RzrNa;nI^m6uZA1#_EBwH}C*;1?tS_J&J`f7BUE;p6-&F>oFY|Cz0Fe;uO9 znQ^Z5S!bqliymz;9!hPp)llGVXw^On&x2~%KZLC{p{l`BjH;-+kdf&JXG=)P=v3OT zwJ}hA?zFU@i>cIz8dZf?8pLtl0_;LLk@;|-N5nu@ri-P(aDa_mg+@!TvT57MY*AI@ z&gu)O>|&22=maqdn!+7cUKl|tf6^{kC=q0iBB-)et|L%6v5oPQ^=0ZZP`PT0`HSpD z6R<%ODf5U-OWX|}C#c)x8Z2n9x;%w$5mhp4FsOk+ri}qc23Qfu-BRnPst$jJGz6rx zp=?#7m|58`XEM_#Y?=#uaONm83tQ#Nc76Y|O=3=bb5+kuGo7!Pk!*A+f7r8;tYyvB zbK4EW9;#@thT!gCTmsmr40^O8qjHmPjEg7(FbJ=< z%%;+k-0;vp&?!FSsui{8`)Uzdo=kPDtaYD1TR@OlAD17Ozb=1E+}}g3-{9YWkO0h5 z7Fwbc89_*fWJVk4e-I=lcvkF*U$7(^%7ZIsCh5)fqxRgXKG=kxs`Q)X{c^P|+yE3! zkq&rKujvkh2|61fQriN==O5fpAE38JAyi3eCW?P>cl6LsN@D;HCY??bv{M(Rv_xqP zwA~Yd$-_}}WvxvAZ`hXpptyY{I2%}hSG z)x6^j<($?zf6Uy4(?P(%7t^L|T@R7;n~eZ|+>07p9led}NzpN>+9^&FOZj^4lyYbZ62vyn`$cD61jT9at4H zjsZ>Pe02Zi@H|D226nXW@>x|9Vxyc*`3XTV!q_!^@S&7f3&4mngaZ_ET8ABbG)%p1;70OcBN_Z}KpiCP3$g-F=h0hVKXqSa%9<8uOL(|q+R}G4rO?M;* zUm@nTpuCt|#pr{ln4H;Fdyi~AMdx31u=FZNDZ0|>Uh-4n8r<~r|IS6zqoaq)5_TO# zf2YhoSU)?+gEuDffqx0 z;3$h}9_Y+eExkxl)WvdQRe>aQx)Eaa*5!lqBGm{}FYj~}IYlAa+P)~I0OLg>`PKJj)MNoxG(+S@Tk%nMK@bhknq^lo}zWegRW=Jkl z{?&US#?g6*d}%MFvc0qyGV{&bf<^1S&5&X#YBa-MNTHkmaw}w){r`U_B$#(}&?CdY1k9o7ppW2? zC7{!(_nfL>+o1Co&6o&IzT4&Be_n+J#3|gz~4>*T9x6CyS03dA7y%4lQ zPaZkJm_q6>xrC9q@wt@ADSJ=sbUC`!_)4#&K%^^3=e<(d4!p8GD;wFTf9{s_-g5|< zL@Av%>@@;Yx)SJ8>l}cYI5-2%QsnTYo2-gjS>7cu=>}CfJPD4nbp%=%F!N3|QjzAH z{PjGJUL;)(&)%Zb>hdkK#(h6>CwwH^`OOS>F>cXqXgokirWaxoL5rd#8K5xbSQaJr?|A|dSi*zB9rVi^UfG%wLfNV;+eE#nbcd9B|vT^}A=#V1%Yxk}PRM$dD4_6IwvJ&MwnkYdiLS%t&+**xSi5 zRMNCEQcj-XbnXH!e`j*d?ju_~7GVA~(bMWK-J3SSB-E6g6At&LD_iAC&iUr+^Yhc= zS@%qj&52W%z*f-x6FsGcHqAffW+T$Y$sCot4w8s$Erbu*ddRd;zTfKTyC#ONs?&@S zs6}}CDwOf5<3O&buiH=?iunyhG|ri!=8;g0A+~HqVsIewe{^xv(CthldZZqk-LL2i zSc1N9)|l@(v&I`@+L_ragru@a8xyKea{v2vL(CXiF@0u^t8A5PY*B~pX?|>SaajRd zK>q^-bb%s&<2zl*@k-Wx6#(uqY|{*vSK*#$^K%#_RmdO_R<}Ml&+?f+#WN?;iRW_i zbh-c1zu-V$f1{-;VbNQqKI;3Gt#T!!{$yjriEB=itZ!HM>dc}feX+&Aa#2pu{WJZ- zqAFKflssA#leM#`uI|}Ip5(x`vc*N$POv_rM6LWXw%>We>jxR@f?6TG!?)8#C8*N|E(t>R#3Dq{sB%`A_bg z>fhWs^*_3^dzNSkc+<5dS^sXuw|QTcHdVvhf9Edor`!AM=TA?MU+zBrP|?Bl$IsUf z`Io;xIDO81&v#|3KV5G5RlTqF!{?`Wm?7W(3E^Z!-%7FDpG?w;hFZxtTb) z85sf0jEr3W6KDtG28bHESeO9h=mFApwm?TXG7&p_caVjdxzl@@|M>`@G^PSDadC0b z{=FO^WCH|Q7#rFG%UrL_$$r(E{oZkh}hZK0BxNd;r`MmY5@Wozi+!c!#^i$ zWozeZ>-irrwXiiY{i_KRXL|-UTMGwgpp@u;Lf%bqe{5zzCjc8GBO?bFD*)&K0J<5Q ze>41*Ue()W?>8WC;Ph@X!;MoA3n&!4WPsLK6^|6#=n04`$_kGyiDwDt=<2G|9!p; z8gfeN>Y_CNbo^hRu&|vQz>|)J6+p+#f5rsh;^YKyurUI>|92KeLyP~YVEnUG%GT5l z!1a&V_g(rQ!7l$ff6D*d4JyF@&Lwa6KD0mp<)0bli~Y45VAG1{_i$gIEq`i0ZkMwoQ%!?8J2(SYJXiaYYSVTqMf6~U#|*)j){@+ z|M=c-ma*0Q%i;Jwk$<~@?-%EPmlU%#wln$bhB33T0}MeRLw7jF_d#N2V*_|HyYJcdx zg8F}nivz%5_+Mgu&tmBKex4SNR)0d?MHu~|cM-<_rN0h=!Q@}a1YiLEe+%F9nf?pk zWtsg8Spf{@e`>$0GAe=h!=^L$<4tLPh7f z1nO{pM1o1As><+R2i*xJj`X6eZi`Xg3N91AcpkUZ!-TZPE44j&J?qCRPwlqBElz$K zoG3UJsvE$E#iLUd-1B;I@KX1)f@}frk|xV>aOOl(%tOC-?U!(?e;X)W84aG_Robg! zmw|sSou-Rai}>PKG)tCilsSh@4DCdR|B)K)!VP)$_HG6(d*Y2yI+g~``y`U3&{KPZ zIqG4~W3613*|GaG>1S&Vdq4~RJL(h>|zrG9^ul-oVXlUj_ zG7!UW>G0h$%cynrJ6uyG8d-H4mYThk_LvTJIozhU#<;jKe@?d>?AjZ1G_m=t2cAgQ z(BRK^P~DoCEecPA`|VR*Ccg6>R|_r_P9#ZUMp{k_OM@c(kiKs1?lC-7deuSOfGga6 z=)lo7MyXoqn^09n4rc~x0jwlUbksu=M9Ait^CyYPO>WU&Xd#=@p;51t!Fksj6}{JE zo!95O`E-oDfApnhD#FMJ^41CH+F1%Qvgb3oQEa$+T=jhzuiQm4t<+FnXX0@Ffp4CD zVSwcj-_m(8+V#&_PY&%6So|H9jC#p#$4RwH1FsaCeTSz$he2WO7M~GDcs{#pk0{CS7F^(tTj_Sh55T)ZcEq z$Z&(Ud0i=(avc4x_2fle@*5+vP%`Kx`s-C}+ugL!&kEu`dCa6g(PyfBi@=3L#kefr60!*Yej2|D=xEj}*y;t4U^ zMzM^_jpA`Tnw**4)4<+z-W)j$>-WSn3QVNMhZnO;e7UlTFKzNL<=04;1T_NcX~ol6 zZqw24yC;8db>dvhTX}!NY-pG3Yt`hT)1kH6Bc=yYsV+qb ze@Ng-fZCLB{oX5P?rw@QPwYmbXRu2Ustp0v)++t0#{uwkIeZ{r;f}xW*S2(4M4eDJ zRn~Y)S%^+QEOc8rMjF0aSRdaDl>2!kHrp+xeZobva<d}1CO`=sZLS;y7e@R~zFuXDz69FRY?~!s#^{T%VwkBzPq@jg@nEs%vGAJ=n|)QvDW^tP#;zZYyQd8NnA_7Q}t6}%abRj%h9AdM7T&-!e}vrn6+u|w&=GB2$+OlHRre-LVK zxTxKvZCSS~dXZ774>cp%T(mBfW#Xfu@sYP1!tIxTx{;3Rb4&@;$IlU5uh06CESwz8VJY<+2gVO8#CJG_IT`1r&JZK)xrhwmEk%%n6<+WS4D*PU zG7hhuo)7#CFh*#QHEm8|Up~D}f8&Lk7XG z+A(2Oz4`GF3wb_}P5 z`2`_fZS(rpxto0}MP&XPe;Lkzmg+5q@=e4Lj^#91w%t4vzFeuTx9n>)4cJ9Ix58@! z!>GY;!GQgl?3t)tOfbw5*5;`CGHE!ZKw*S$qr;?ST84Agl9;eUu|8&upQMx$K40_x z;(<CR8P} zTGSa<%c_ADwJJg*Wo3zORv$7uoAM%^ zP7kG6#fgwK5sM=?6p&gd?`SoD6ah;6wRBj_v0i>E!lvGMvh0< zsVjuM1vhEPt#bExf3(HJHU+6GG}ua{xZ2b~gKjDDC4k)_%npV+#h!-#&dkzUI|oEy z?t~jXF2KUgTh-6l>iH-qGfU9!oOCd6p_XcSi|;L?w- zB|lChojkZ*g+9PeoIrI%&51NHLiu*R=U!F)nmXec$UuAN`62#w1ndnU%68W)B_%EqaA zrjpx4p5#2)e?LD#b_RFPP}wb~k0|Kbgs4Lq9y}r4Cp4{r`Z5RFuWN^EF9%DdazNAs z-D+@qpVU_>yTZ9#0vVU^Q|I9-y4!mvSW zGxCuce_(NP1@b|X5t|A66t#jFEL#j>jhd?YdS@C(Q8uuoV!qkBZtfbU@7u|5EjEL_zgaf1h;;ztdYrE4PZK!DOCa&-j=6H}*j^ zMltO{C)(i`4D`=bv+Yv1A-q;8KjwXIo)yVPrK>=XecxvN+gNZX$gq ze?a?8zX_<;v+&sjA#R&Cep>KKmu9gswj*3*1=*wJ))v+C!y%*ciCSfs%w+@~E%}_^ z@*yr^^%tDMikQ`%$vh@m;p(ypG!p5ZTJVO8?(5gS%VvCgEG$OMF#rbGxMq+v8fvTz zhqkOJv0;AVjp?2gQ!$>vtff;Q#Rw-ie*W~okSnL4>p2OffDjvn)3ESVM(meYL2=_JZ7=r z1d(*I-1`x~88FM2D@X0o4HuI>B6Dv~GPykrC0q^f7{o%WcPCzwi-|#G`w27}f1_X{ z&EI1nh^!97-=iQx_eox;WRs5z z%Z!chR71~gY?Knp&9A=yZ8=NKhoHJ=*@+Vi#all6W?m{v^haPqTuNA1e_2`~VHc5K z4E6}bFDPGPU2pJ4ag45k?${+URdnWI8)XY#*zdg{7Q;`BL&pO|1g2YYLkCc8;VK#A z8knByxbt9@&@1|Hh^0sy{Y4E7$*CH~aDIrpO!13s+3vBNb+`-&t@F_8t=m;aH%mn= zeOG?|nJCnH;@&ufBmxMEf4F;5&AxQd&*xv0aBTX%T3#e%#C;TZ!BZau5QfQIEYhRE ze4)_FyBmzU(e@9|Mvue~3!V4cL*pWc$>XrISMQZ#qewR*Y5Kqo&Qgn&Qf0oF)x zEW|+-N+sP#bTBHJ_As=@-ob`H5q@4jPtO7pscd;MN5zXz@6Wq5!be=sK@O^tj$+o=-C=!J10 z;x=g4RcIj};VUC3FPIW>jFIx&w5oq+?ID2qS9L+J#XKmNV+tym3@;DeEqrQ>wUFc6 zDd*3o(?H*vkl{QwtFLq1uN|49uJ*)bJeHT$LLVM0V8;zql)p4;eL$+ApLUhsz ziM85wM=$&Of6G~tz52=JfY^La8*pxqJ~de z51Qgz3JHs;#I=oiso!Ta7){Kl-PmN9z0d>kunFLBfBjgzK;~DiOF9*?>mA;h`>&szqGJvP|oMfV|?ZB5HJ>=JCY>z ztmg2Pe?j0mxMl-i+u=An>E@#rj)FSIFa+-@y-(7ypTqV(sb)Z{!pkLR1S(e4_#2$t z&xYn2ctm)#}W74F>!TrSIJ>zv+h~tt^Tk08Ix0aH`&slTK?BhFk1R__D zZjFAxy7MVuk{&NDp^jXr?kbVhNp0$nbfh*mf4dc2{Ay!m>QXGix%OiSJuny%kxSKH zTQFJ=HDGiQvM4aeg2TS4jJYw0$9wm& zf6TN~$`x-erm)iv#_k=*rIgTq=-o38xzA>ZbWohKlw0@g)w ziwa$$>Un!t5t|IT)>M})TEWTBC%R>Ke;eD{v|Y#wW-Awse`G#8XtROPyHPMyf1;^& z`wgn(S_6Z8znBV&m9i{)r|Ri*zK0dCx*WwM016!{x)d0iLeN;gGH|&4I7q zR77iFJrs4okB5fJT{&FFmiFM^xeH&$hF_uW1_@WLqpodQ)!|BL3srU6uc7mof94GX z&nX5#QOli6P2+qtIrN84`VG^x6u#ek6{(i5CyTN`)HA4?tQtY~3#2*lcxs;bbPFq0Ih++UmHEQdIdGkvY3n@0@SUej{Qk4m)@Xf{mgJut;LW}T;rRXd*%aa zd?S49Z%mf!G_5hBMKzrXG4iq`;9CfcnB^dV6^OfG)WC&>`*jQD6-_tJ7NLV=FPvgw;!t3j!l=1fIG&}_1UXLK$rE1)YaxVaKrgSe{ylT0ga(`m1jIX zL~0;IS_Rw{T2d$NerG1h!+8Vu!B~y8CJ|f>HNFocmaB~YX_f|J*N0fQNs?C zsP4@yh1TLJ+(L|m6-{`nwhQ&9qiAP{ko$67=5}os;iZXUYp8iEYZz2&A%=S)_~b^p zyfyK4R+L#bABC7{f8+y9j)}Kiz{J^M#ctzt&gqeJ$7i(V<^4r*@wa>T3C*!FBl5in z(Jo~6`Prtw(X&y?Y(8Sn@YBEq2hgy#xz7m}WB!85jG7dlJJZo3Y;I4ug)PUYGk8Xg zNDNDey&Q`rDlDS&D!ElwmjkiGnvkMeM*c2ytpws^w^l@+e^Qds8_?ffAv$%(!x>;- z4*P^=9o*GN+cNE%1CVV!rdEK;nH#<&whuNt7{3&%Azqh)v0Zyz6`ZN%hwhQAz}$?( zGgIHx^Jjaw$|ZXpIIEdQ3MNolF7w8(t`>ae(r@wt=>1gEplOD&1j&&*!ehVmPw94N z8fmQO66MMNe=4+J!ZhqVV~jJ6ZnutJvu&G!6BCU@sJDkX5CL&tR$t?z?#`Ylp?u)W z-vNs~z}Oni)M&tk{KV-=5Nji%w%Ekeq~!F9yPc_18LquviiT=Am{#wl1*ybJIJ_sq zi>VEqSUkhB+u3~-4RXAm=LDaSEPKpc`S3&TMl$)Jf8`=)p9SnR)$+Wp!YioHp7SDT zo{#X&0aakSJqW#tPfQF6I0?zW#hxYO=WWV->1r6OYsk-UDL&Fv6Z8s~oRwVq;@BS4 zI0@E#1sfhW4Ac3n9`lVY9-`yvXF)#?we6S@>6J>Q%c=SEPdZ;t_pzTbH5-kuX9Ro~ z7LF_3e-VTr%1j2tb09JpCxv=w%cFLfEF%YJsw^hr5M^jWh};r!X-|Bzf2*ve`gnbp z?O5tlOr1JZwNq#OgB0lKD`ls0L2`#A(RNcK8>SZzEBNCiTMH3_MwaQBj`cTY$D-Vi zy>YrHMn7#Hi`DmJR=`Nm<2plyaj5LI`%xgZG1@&T>OGndM+Jw!oTR{P;n-~{2?gz0 za(^N?B}{FWvdo?+N0*{|`RO(ZzmD)k^gRTS3(f<3qg7}0P){&Mff ze?>ebE8u{ccs(z>-Y=wT$j08%$`4bFSi6Q$Q%mvrQbd1{X)MpnLJ=K2YFyCD)sxuF z!jwZd_9QZf!oQHqWqglDu#OEO-`vn?Pz&b6wOE@W>txeExxs*QUMf9Jg7i`JsM*OH z`XU;HV$L+xeAKq`3oVtS2Neb+AW*72f9cnrj1?p($y3^}B1gBeN=f9r!NR%f#{o)7 zKPhDHMANhvpdL0tC0QK*oiG^2T&lV8@o>hj$p)`z)MnG=)G0)Z!n)*`mzgwBAZIJX zz8hQTw&gBOLQsZ@pyhr%JcJk{{KoC{*NNb%ZrAlMPFAw9ThE`X!;4#bFbk%gf7iOO zM;lNmZ)63S65RBO>nF!0bYo}FkuH2}TIVdeMjah;G3d$%UF`aMH`I+*d$(}(!g|PZ ze8Ou5W%n?-B~QT;TTyM@{eet+JLZNm(QnphXn-Eae>kvELpy5R-#$0&HGWtz)ka0+{mFa2vbLhvDwD{+ zixYn&x(B^u3wPFOgMtV~^`L(s<5W;fpQ`pne}oCq5T+l) z&TC1bU_a$M;$!?)uOe#XNKmt{=AgTdNio)g?1{*)?}ZvfwFq6qbWU(B_HY%(5D8*s zr7|zoPeEOL<*P1(tapN>9H_PKvl7ix_~!OjlxE~Av(FPsf-lNh89b9)DRNA}v8!vV zsA9d6$9C6jE&m#9IPV_ zC^~?@JQ3=b$13v>3ix-Cw<_3YKXS;hPr&N2SV-d7mW+d(3PHr4uOCm^~SNa+m==+F$v7;e?A7w0VMmSmVLf0mX_XBkae^^lgAEXP!SOism z8s8BlS`XY&$M1Jx)1{`@cBo@zjpGdas<9VcWU@mygE7C1*MYH`4n$0zbBNCfph`3@ z^avvG@6C&@eCfR&hEBK{TGPC8MPz;!6IpY~QG}HiXeYZMOG7n<1#i5xV>?#TB0gDS z1%zw(0@r=-FPo>Me;vK9zmc0X+ykO3RM3}`RDO5rTINMh#@ZHLe*E4tFUl;(LChU5 zlfMiiQ#8dDTO_P`!|~`YVaW3COQgjS>2qK0vl?@pGWHPc>~Ealhav5>-1<-&MzOF`oVQ#qQa1&=0yIV@WqMsP^NDpO6i0uBv(h`8OjUMK?_!;ls@IBd!Yx zN7;ztG$r`ie*_XhEP)WJ%}e(^V){i$Hywtv;7hKP2l{7Yx$ZZR*_cEH;$1!^BU>bU zNqrvwH>Gv%FHouY^&E;SyEY%3j^ykvkpMB$VI#`EzVy{zoy9u=47bG~v^dn~sC9(! z@`EsrwJa;5O@fXA`v4Za4EbF31O;C(jEoCUzmUA{e?>7fAE>PZ#X;Hc-d=GmBLfq) zSm&SWu!epm))PSa;WQoO5uiyBapD@>#C|(2zf(?Gi_MJyAIfEJ@GiM`|S6{5w$J8-M*dS@=XxKZtcnJWX zWSwFXe+sQu6Q2OdMYybtT0+}-`8;tlU$l00OfB^M z>_+$@oTx_fP+m;p8_P2Oj2&y+*Q9DF4|3u!f3;8xW3oT?X2D$W8Ovky_7;fmLD_-5 zb}>(FL}Wby{&v~&wAV~_a!Gvzq$7dxrPQqLlUkrVp#IO zfnJNj!Th~&4_}z`$bSZ6AGtb1Sc0aj=069ybE_aN>Z7sU2eZT`(O{exL%PzOjk=pF ze`O(7ErQG>!=vM=SL27SD^ZsC2E$*<^W7S z7f+lxg^|~j>dPBy45C6E(bdn6p^A9ee|x$3cwBce8lCgmL1$H$(##%xmFgSh)1qb{ z-%`gHzXw^dj_=e-Bcd z--a_{HZ&})U51ln{_>mX#~H49gLd~fECPQyxk z-U9h!H`%tcMIz>#ju9&0SSHU8f8r{Cq;Gz~ue2s=RTc#Z;HY!jA?sGx}Ib*1J}?(xQxUl z$)$~>`aPI!a;j^YzmK8jumW%bNbu@>I$33TfOzHmd@>95_<%G-WzRh$e|p10>X@Z3 z9mW@Y!RsAqr|v%YweFv7mVG~RDQwCaJ!Jb3tV$f+VV(v= zg%}Yw=H3YPG9CkA`dpE6sH~i1`qTWqsV5`qG^%sZh}^9|&K*F0l&q*ZNwj&Y;Sq^( zyP0Yv$t~msA6M2FIHd={FQKo3S`*Z?QBjfcWryQ#8PTa+5 zQeAN*w6&sj`FM^Bk_yBUnVQlw%i_eg!>26_7Y@3f&u)+ITq!rze`rxX@2;K#ozl;R z;*2LVxE1yB)Psb@B2cTsaL-ame2ZRaYVLX*fP~ zEGk`N3(kr`qIbhtk~^zIYHT6MkFFg)nkV{LhW!&Vhh+Dtgj^v=`e`MZKe9Z^cH({T^M|4f1)!vbkI3jBF{SEe`}NJjCs(Q9duXNjbQn3A(6oK z?2Ai|QiW2Nq5nd$C&Lp*U5T99q51);27Swb%QtIc8lCEaeyRcn?-_mbcK0Vk<0pZB zIctcHI_g@JI-UdS_QRrVxhn)WZ9DP(vLZ?-$}t=ywQ@D5?&ZGkY)nVuLFQRCM>=t8p>3qXlniN(Z*e-qZ5zEpwOb&A? z-bxA-T4LuwYl(7@SUUhZ-%r;3<_}L->#h=}ELlEp$1PGIH2NzeB%_3 z6_%IymmxUVdATKBpFNBO^PhexaRjbDAPf9a%TFxjIm%}+xqVMJq||2HL-_?tj({;Ycc zO%}?RPQO@|W8^mDACRUinlM_e3)F6BjTx*+hiCAgeD)n6PN~t*Xk0%=SVw#uV3ys5 zeGM9B9TQjeHKp|H8EC~jS@&9pcaDDHs~jyhSN8@t`S9aIc&h#5pyw49pk_fUe-$U2 zw)EpgmvQOc@Z+d*U;gsw$27_^F(c=e$pg^wvsMLJzZksqWB3y?1g+P{S8Mq_a#G{z z);6&gA+sRC>o26qL)7dC)+uQsBFl1vJO#Z_9f{mS0vMLzT1Xv9!`tS{_S6s>!8$+> zAi5W+& zrOI*U@}TRuhkld?*4Uj`hzGKS3*74>S_{LsDUhW+j)@$wV-N8ma-b;?jy z*azJPp7q_goeSGr{j%vmfT2IqB*(1YDwL@#Kt>_YGRX}{_|7Nc-CT&^aAz%o3 zR}q3L;vfR1LTd!|y?6R$16{CtV77R9PS=nn!|}HU+YPov5`!WyWIt+3ZBtaPe#HYU zF2^u=G2?FtT2gb{Nt>^%fAH}79&F%Est=Yi%4v4{YzJfK`@@|9Y(CMa3R>u3PuXKlf?eG z)ZgwgoAH{$HyK=O`L`Z^OQ6qP>^FW-aHNdP4}D5f+ar++t7+Nlf7@JuX?y9BqDmh% z%)M?2_T)_%l<73{!!@Ir>e)~$)nv17BMKuw_^9QUeA&L?@zUQ`PGxp6qGPk!)7D01 zy?qY94B3e&Fc{9I{d*~`X0oHHJZ`Tlyf$99e=MjWR^CaAUPw%sw-)Z}FNh_Lyv!#1 zl!#5UYDZ%3K{T&*ULrwAm;- z_a~1B)i1I1f6rN8al^M{oA{7oPT@<wE9K>JKoS#!&FzGSQ^5`fI)h@O%0)B{*- zl~(?KC!fy89IJYVrL^At=MdH z5;#OzL~+W4OA|EXgq@auF z!p*w9!7$SF(W_jTm5Bm-dG}`%zra{{AI%VN0d?qc;Y##Eyha7$rY$uqwI_xy6RU4u zI@7}!JGtI;e`cd04g+p+!7Sk?D6-hOvf7G-qqEo68ngtJm0KsD^38cq()WMA zBTz%NPi@qYYaS^(B9_XR!VWbQ0Ra#G@Ry<3tYsrpOBCH^H;su%zvuSH6WFgNp0wFQ zaC#J!ZI7 zgjnJZdLuP46E9xoE|e^uaebSAgSRKT!aljN+hiOzECSXQCJ+}McC!=bgr>GZ#?v9p z$$4AL8ZUN|0)>fO4{bjS74RzLz%HW7x&(H4E(3e64S%3_)(l`e#(6PdaOf$N(nMN* z239h6$D&>rX$s{R3KOZ%A7FL1T`{&uJb=NFa2xF~(;hK)<=h*QxswS9>#5H%zc|Z) z^E-CYYeiWfMDYq|Os$jYoY_Ec8~vh;3;{O$F5+cV57V%1{-)woOD`(bUnDScA4a?VX{u7)Vn_7z`udc^uG+|-M`WM^@UH^n2q1C~# zz=H=&m|!disxy}``t_gD`%CDma5y$3(^T>J%#@wu_ZbWh3%#(JXFmH{SLhFkOh)t$ z?m7*X)rG(}0Zb`0Q+bl!R*a#;T`!87<)*vX+kcMwwcT$*BF#5mQA7ClYmnBS-Xg9G zYMX2sIM4c|!92?aO3B|tr({-sT@O!bhms`D6UrEq*;E={@hoX@)3jR=4S;O4no=sd zaJsg+9oFG9eEIss(-8(^QQ^fyZ^%}5AGGraV4)ZLo3N-4avKDgdkua2=)?8~{RJWC zZGSiq6y>1=)h^??Lf8i(ImrjTe_|nCmEv^znWFqE6?u-ll!lr~N+b&{c8mYgwlmhB z-U#-3dDjX~_4RSpX0C*$Quwnm%Y+VKl>bEm+5e7t-qI$C$K`MfIC^rcj@}o2l$`u^ zT!fE^s5E$HiZSCGh?NJk7TZI?ieqL*h=27wSMEn?eVpeNaCF*IZS8 z@l>?3jze7M@^y>q>=YjL@wn4nxz)Nd8lDao2TN6fLM&lx)P6QmfO`gEPfUIGM}H$G z1E$sii)7yi&yB;1U2yHK_VKBef^|w^NmOp3z5UjZPZA=m{xA6u81Thx$1i7jP;4`f zAA*hc(Ymt=eED;~wJ(P*B1yg|o4T{aVlE|+Shj|qwf~mdB*{aaSSkW1J>G!P-o#Bi zOA_45{-i}CQNT65+MTlufE7cr@qb8b6odyOGsOEe{(0Hgvfqi^=_~4T!N+`-)J!iY)`Asx$BXdME-pvC4%r&ecyMU?q z1D%*Nxk|isApGIdlZ?UgvD>;sWkm-#?!d1Mb zCfsfcj}d4$_Vx(2xJ-o*IO8cRy{p zeNP1u*kkdzL$oo4g9g3J=dUgGP<()qS}aoDa~2>JDh61!uqD&K6vug$Pu z&b)m*4Zo$1MviT*2(`g;Fbj26W&ly_6{QGyzur7TL#H83PDxdkB!71P>cVY^Eji$r zH+BNA|V0v9b{Wg@wpsf67q;F|zhrseO5Nz)uu@J53qO3SL*IgmDm;;rK;UDo{-<7qG%!JW zLH|3!B^IF;*EX)-s-tQt<0W{}=udIa>Cj)Sp^uI4lwCd(#(zW)W?>x(;ROsh50cB= zeDHJr%9kVIsnagFtz(>#^Yy{P`+$)19BZ|MgmLiN`_5bf0UQDxIz}Dh;Q%?#X~9_%lF)@L}Fl0dJdEBafNiwY~lUdYP6A{wVX=dxXE)+!|}i z*=60#saDn`Q-7{ynv-|UHjIS4KlS_~s3#yaLhndC)s&~_0WxFy_pc5PF8TqJAO4hg z$lC?&BV`^O2hJYDXJTp3Q>Y9R(5=Ply$ra5ppeiDF69g9Av&s6_M+4F-EW|iPmD(b zYCs0$z587m1L!hsO7v8N&*LZP#S~yU4=&ZZ@v+ltu77dfm^jExCxs`U!OnxE+*m$b8&CTA%nI!=U*% zI2SEeu9SMiu|M_HO8MOVrtdzb@8wOO?w{uIK8;!HCTesqMY{n&#!|0-EFIp3={ z?7AtR#Zon|kq)ltD^c*ek_gv64Qhw5qp4y@YJZKp0C&mYjq5t9tNPg3^>R*7uP+gC zrJ^9lu1#o|G{unBFM^it_(pArs_jX&NqEM>O#OnW1kTLXL78M6JlIbd@)b`Jq*chW z)e@II;nl*}l}hIEUGCG8mdl8@$f9JOsKi-YraQl3(IrH7;h}lW?awi|yR71}CZB6Q z-hUS6gyp(^==4J5^Li}8jeF9057a@WpWgg1 z%n_~Pbg=3dgEQ34x|pSoK>;-n6U>xjQh)A#*~mr9#M$bQsM59Kmfa&&642j*9&+Ab z^k0H%W|rd01;xoyNBCi(!ke|9ysk-a$~j7EtKT3B@uH6Te-Sa<-x?t&yso%SThBk& z@1V6!80{P%LuxL3)e5bPzW{aW*S>x+F#51<9_jA}H5AaetyLvA-voN}3+mAi@PAEb z?=(3an^*lE<6qTQA1ihV(@Cw9A*LhIMwAsxMx>r13Qki-0!KbntU&7#H|Rv%#mus? z!`D_}!3LS~k*9huVN~(rbRzdS$6x%dG$4Eo929tbNKGtl{&~PsFL{V#JQBkws!SO( zZ$%GyfOBUyKA`6(i4LQ8K_R{p6@N2IU%y}j>XO70@>%Ejh7%LaTEf|OT0gm)g+eY4 zQo`b3^FCoc^KP=m9SY|{*D}ph3D78{bPUB$Ai{!kW`kJb+YCU0_yVx0O zkkaqft~sD7Alz}UAU!U1)p-YIu`j_qU>cD5Y{Xmn#?$KF(k)Raf+4&0D}N_&L_smt zeZ|I9L+Quzo4C|7@4V}p=IWbAQYxA-h3&?GjO#Y?{Lima9xmiCP(b69p z23^#-ExbK&D+$KCUZMlGB^2Ok8FkZ3D;HYp*r6I(P8HEFieM6t`@-|uXf(rzCoC8v9N z?(VUDHiH;Aq#%IBAR);~XCiYJPdG3RQf81tkrq&pHzy!{jHl7@@yW@%0Km3-YP;$Y zW_YKp)!iC!s9)+*kI(ya@Tdsy-&zd>f2eeXgJx~@+8W4+!#KjRHqE9hwIK@LaibTt z>Nl@2i^(C0OIGlCMCl#n5gmsLg@LNAnr_mnQqx)}06Q8ym}jr{d}5W=z`P^E5fK35 zjw6=R5}lK(;a%616K!EeC59uwd;Mx*?i$UKyy!{DBqPZ)4uZ>G;QS|oXKEwM`CW)N0bGFUhp+ZdLv6qf+ z&_7~Y0>owkVrE7L zeh`}-h&h0ml~Je!#AgFyPDbx142=F)8330M1&Rt~Ze(+Ga%Ev{3T19&Z(?c+b97;H Vba--QW(qPeG&45}B_%~qMhXb^bmag5 delta 17015 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 From 147480605d65cfbd720583cdfd9dc02979241e73 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Fri, 28 Feb 2020 09:30:20 +0100 Subject: [PATCH 7/7] Trying Gitlab CI --- .gitlab-ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..f73ad4c --- /dev/null +++ b/.gitlab-ci.yml @@ -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