From 30f3d61cdd3608617e0a9cc07f09a325c8196e14 Mon Sep 17 00:00:00 2001
From: Alexandre Grison <a.grison@gmail.com>
Date: Tue, 9 May 2017 17:25:43 +0200
Subject: [PATCH] Fixes

---
 api.iml                                       | 63 +++++++------------
 .../realworld/exception/InvalidException.kt   |  2 +-
 .../io/realworld/jwt/ApiKeySecuredAspect.kt   |  4 +-
 .../kotlin/io/realworld/model/inout/Login.kt  |  2 +-
 .../io/realworld/model/inout/Register.kt      |  2 +-
 .../io/realworld/model/inout/UpdateUser.kt    | 21 +++++--
 .../kotlin/io/realworld/web/ArticleHandler.kt | 13 +++-
 .../io/realworld/web/InvalidRequestHandler.kt |  2 +-
 .../kotlin/io/realworld/web/UserHandler.kt    |  4 +-
 9 files changed, 56 insertions(+), 57 deletions(-)

diff --git a/api.iml b/api.iml
index 9d1ea10..4e66be8 100644
--- a/api.iml
+++ b/api.iml
@@ -5,48 +5,27 @@
       <configuration />
     </facet>
     <facet type="kotlin-language" name="Kotlin">
-      <configuration version="1">
-        <option name="compilerInfo">
-          <KotlinCompilerInfo>
-            <option name="compilerSettings">
-              <CompilerSettings />
-            </option>
-            <option name="k2jsCompilerArguments">
-              <K2JSCompilerArguments />
-            </option>
-            <option name="k2jvmCompilerArguments">
-              <K2JVMCompilerArguments>
-                <option name="jvmTarget" value="1.8" />
-              </K2JVMCompilerArguments>
-            </option>
-            <option name="_commonCompilerArguments">
-              <DummyImpl>
-                <option name="pluginClasspaths">
-                  <array>
-                    <option value="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-maven-allopen/1.1.2/kotlin-maven-allopen-1.1.2.jar" />
-                  </array>
-                </option>
-                <option name="coroutinesWarn" value="true" />
-                <option name="pluginOptions">
-                  <array>
-                    <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.stereotype.Component" />
-                    <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.transaction.annotation.Transactional" />
-                    <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.scheduling.annotation.Async" />
-                    <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.cache.annotation.Cacheable" />
-                  </array>
-                </option>
-              </DummyImpl>
-            </option>
-          </KotlinCompilerInfo>
-        </option>
-        <option name="useProjectSettings" value="false" />
-        <option name="versionInfo">
-          <KotlinVersionInfo>
-            <option name="apiLevel" value="1.1" />
-            <option name="languageLevel" value="1.1" />
-            <option name="targetPlatformName" value="JVM 1.8" />
-          </KotlinVersionInfo>
-        </option>
+      <configuration version="2" platform="JVM 1.8" useProjectSettings="false">
+        <compilerSettings />
+        <compilerArguments>
+          <option name="jvmTarget" value="1.8" />
+          <option name="languageVersion" value="1.1" />
+          <option name="apiVersion" value="1.1" />
+          <option name="pluginClasspaths">
+            <array>
+              <option value="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-maven-allopen/1.1.2/kotlin-maven-allopen-1.1.2.jar" />
+            </array>
+          </option>
+          <option name="coroutinesWarn" value="true" />
+          <option name="pluginOptions">
+            <array>
+              <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.stereotype.Component" />
+              <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.transaction.annotation.Transactional" />
+              <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.scheduling.annotation.Async" />
+              <option value="plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.cache.annotation.Cacheable" />
+            </array>
+          </option>
+        </compilerArguments>
       </configuration>
     </facet>
   </component>
diff --git a/src/main/kotlin/io/realworld/exception/InvalidException.kt b/src/main/kotlin/io/realworld/exception/InvalidException.kt
index 5365198..d8d6339 100644
--- a/src/main/kotlin/io/realworld/exception/InvalidException.kt
+++ b/src/main/kotlin/io/realworld/exception/InvalidException.kt
@@ -2,7 +2,7 @@ package io.realworld.exception
 
 import org.springframework.validation.Errors
 
-data class InvalidException(val errors: Errors) : RuntimeException()
+data class InvalidException(val errors: Errors?) : RuntimeException()
 
 object InvalidRequest {
     fun check(errors: Errors) {
diff --git a/src/main/kotlin/io/realworld/jwt/ApiKeySecuredAspect.kt b/src/main/kotlin/io/realworld/jwt/ApiKeySecuredAspect.kt
index c51cb30..63d0fb7 100644
--- a/src/main/kotlin/io/realworld/jwt/ApiKeySecuredAspect.kt
+++ b/src/main/kotlin/io/realworld/jwt/ApiKeySecuredAspect.kt
@@ -116,7 +116,7 @@ class ApiKeySecuredAspect(@Autowired val userService: UserService) {
                         if (StringUtils.isEmpty(e.message)) rs.reason else e.message,
                         rs.value)
             } else {
-                LOG.error("ERROR accessing resource", e)
+                LOG.error("ERROR accessing resource")
             }
             throw e
         }
@@ -138,4 +138,4 @@ class ApiKeySecuredAspect(@Autowired val userService: UserService) {
     companion object {
         private val LOG = LoggerFactory.getLogger(ApiKeySecuredAspect::class.java)
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/io/realworld/model/inout/Login.kt b/src/main/kotlin/io/realworld/model/inout/Login.kt
index c00a949..45803dd 100644
--- a/src/main/kotlin/io/realworld/model/inout/Login.kt
+++ b/src/main/kotlin/io/realworld/model/inout/Login.kt
@@ -9,7 +9,7 @@ import javax.validation.constraints.Size
 class Login {
     @NotNull(message = "can't be missing")
     @Size(min = 1, message = "can't be empty")
-    @Pattern(regexp="^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$", message="must be a valid email")
+    @Pattern(regexp="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$", message="must be a valid email")
     var email: String? = ""
 
     @NotNull(message = "can't be missing")
diff --git a/src/main/kotlin/io/realworld/model/inout/Register.kt b/src/main/kotlin/io/realworld/model/inout/Register.kt
index 006a8ec..fee6ae1 100644
--- a/src/main/kotlin/io/realworld/model/inout/Register.kt
+++ b/src/main/kotlin/io/realworld/model/inout/Register.kt
@@ -14,7 +14,7 @@ class Register {
 
     @NotNull(message = "can't be missing")
     @Size(min = 1, message = "can't be empty")
-    @Pattern(regexp="^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$", message="must be a valid email")
+    @Pattern(regexp="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$", message="must be a valid email")
     var email: String? = ""
 
     @NotNull(message = "can't be missing")
diff --git a/src/main/kotlin/io/realworld/model/inout/UpdateUser.kt b/src/main/kotlin/io/realworld/model/inout/UpdateUser.kt
index 1122ed0..a619a65 100644
--- a/src/main/kotlin/io/realworld/model/inout/UpdateUser.kt
+++ b/src/main/kotlin/io/realworld/model/inout/UpdateUser.kt
@@ -1,10 +1,21 @@
 package io.realworld.model.inout
 
 import com.fasterxml.jackson.annotation.JsonRootName
+import javax.validation.constraints.NotNull
+import javax.validation.constraints.Pattern
+import javax.validation.constraints.Size
 
 @JsonRootName("user")
-data class UpdateUser(var username: String? = null,
-                      var email: String? = null,
-                      var password: String? = null,
-                      var image: String? = null,
-                      var bio: String? = null)
\ No newline at end of file
+class UpdateUser {
+    @Size(min = 1, message = "can't be empty")
+    @Pattern(regexp="^\\w+$", message = "must be alphanumeric")
+    var username: String? = null
+
+    @Size(min = 1, message = "can't be empty")
+    @Pattern(regexp="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$", message="must be a valid email")
+    var email: String? = null
+
+    var password: String? = null
+    var image: String? = ""
+    var bio: String? = ""
+}
\ No newline at end of file
diff --git a/src/main/kotlin/io/realworld/web/ArticleHandler.kt b/src/main/kotlin/io/realworld/web/ArticleHandler.kt
index 4351110..da33df4 100644
--- a/src/main/kotlin/io/realworld/web/ArticleHandler.kt
+++ b/src/main/kotlin/io/realworld/web/ArticleHandler.kt
@@ -20,6 +20,7 @@ import io.realworld.repository.specification.ArticlesSpecifications
 import io.realworld.service.UserService
 import org.springframework.data.domain.PageRequest
 import org.springframework.data.domain.Sort
+import org.springframework.http.HttpStatus
 import org.springframework.validation.Errors
 import org.springframework.validation.FieldError
 import org.springframework.web.bind.annotation.*
@@ -147,6 +148,7 @@ class ArticleHandler(val repository: ArticleRepository,
     }
 
     @ApiKeySecured
+    @ResponseStatus(HttpStatus.OK)
     @DeleteMapping("/api/articles/{slug}")
     fun deleteArticle(@PathVariable slug: String) {
         repository.findBySlug(slug)?.let {
@@ -183,13 +185,18 @@ class ArticleHandler(val repository: ArticleRepository,
     }
 
     @ApiKeySecured
+    @ResponseStatus(HttpStatus.OK)
     @DeleteMapping("/api/articles/{slug}/comments/{id}")
     fun deleteComment(@PathVariable slug: String, @PathVariable id: Long) {
         repository.findBySlug(slug)?.let {
+            val currentUser = userService.currentUser()
             val comment = commentRepository.findById(id).orElseThrow({ NotFoundException() })
-            if (comment.article.id == it.id)
-                return commentRepository.delete(comment)
-            throw ForbiddenRequestException()
+            if (comment.article.id != it.id)
+                throw ForbiddenRequestException()
+            if (comment.author.id != currentUser.id)
+                throw ForbiddenRequestException()
+
+            return commentRepository.delete(comment)
         }
         throw NotFoundException()
     }
diff --git a/src/main/kotlin/io/realworld/web/InvalidRequestHandler.kt b/src/main/kotlin/io/realworld/web/InvalidRequestHandler.kt
index 828fd76..31743fc 100644
--- a/src/main/kotlin/io/realworld/web/InvalidRequestHandler.kt
+++ b/src/main/kotlin/io/realworld/web/InvalidRequestHandler.kt
@@ -29,7 +29,7 @@ class InvalidRequestHandler {
     @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
     fun processValidationError(ex: InvalidException): Any {
         val errors = mutableMapOf<String, MutableList<String>>()
-        ex.errors.fieldErrors.forEach {
+        ex.errors?.fieldErrors?.forEach {
             if (errors.containsKey(it.field))
                 errors.get(it.field)!!.add(it.defaultMessage)
             else
diff --git a/src/main/kotlin/io/realworld/web/UserHandler.kt b/src/main/kotlin/io/realworld/web/UserHandler.kt
index bb7c2ab..2f73159 100644
--- a/src/main/kotlin/io/realworld/web/UserHandler.kt
+++ b/src/main/kotlin/io/realworld/web/UserHandler.kt
@@ -57,7 +57,9 @@ class UserHandler(val repository: UserRepository,
 
     @ApiKeySecured
     @PutMapping("/api/user")
-    fun updateUser(@RequestBody user: UpdateUser): Any {
+    fun updateUser(@Valid @RequestBody user: UpdateUser, errors: Errors): Any {
+        InvalidRequest.check(errors)
+
         val currentUser = service.currentUser()
 
         // check for errors
-- 
GitLab