Skip to content
This repository has been archived by the owner on May 5, 2022. It is now read-only.

Commit

Permalink
Merge pull request #320 from echo-webkom/develop
Browse files Browse the repository at this point in the history
Release 7.1.2
  • Loading branch information
bakseter authored May 2, 2022
2 parents b6ea2b0 + 662ba60 commit 55eda01
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 172 deletions.
19 changes: 10 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ val exposed_version: String by project
val postgres_version: String by project
val hikari_version: String by project
val kotest_version: String by project
val ktor_rate_limit_version: String by project
val flyway_version: String by project
val sendgrid_version: String by project

Expand All @@ -16,8 +15,8 @@ project.setProperty("mainClassName", "no.uib.echo.ApplicationKt")

plugins {
application
kotlin("jvm") version "1.6.20"
kotlin("plugin.serialization") version "1.6.20"
kotlin("jvm") version "1.6.21"
kotlin("plugin.serialization") version "1.6.21"
id("com.github.johnrengelman.shadow") version "7.1.2"
id("org.jlleitschuh.gradle.ktlint") version "10.2.1"
id("com.adarshr.test-logger") version "3.2.0"
Expand All @@ -32,19 +31,23 @@ application {

repositories {
mavenCentral()
maven { setUrl("https://jitpack.io") }
}

dependencies {
implementation("io.ktor:ktor-server-core:$ktor_version")
implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("io.ktor:ktor-serialization:$ktor_version")
implementation("io.ktor:ktor-auth:$ktor_version")
implementation("io.ktor:ktor-auth-jwt:$ktor_version")
implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-server-cors:$ktor_version")
implementation("io.ktor:ktor-server-auth:$ktor_version")
implementation("io.ktor:ktor-server-auth-jwt:$ktor_version")

implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")
implementation("io.ktor:ktor-client-logging:$ktor_version")
implementation("io.ktor:ktor-client-serialization:$ktor_version")
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")

implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")

implementation("ch.qos.logback:logback-classic:$logback_version")

Expand All @@ -57,8 +60,6 @@ dependencies {

implementation("com.zaxxer:HikariCP:$hikari_version")

implementation("guru.zoroark:ktor-rate-limit:$ktor_rate_limit_version")

implementation("org.flywaydb:flyway-core:$flyway_version")

testImplementation("io.ktor:ktor-server-tests:$ktor_version")
Expand Down
5 changes: 2 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
ktor_version=1.6.8
ktor_version=2.0.1
logback_version=1.2.11
exposed_version=0.38.2
postgres_version=42.3.4
hikari_version=5.0.1
kotest_version=5.2.3
ktor_rate_limit_version=0.0.1
flyway_version=8.5.9
flyway_version=8.5.10
sendgrid_version=4.8.0
kotlin.code.style=official
51 changes: 12 additions & 39 deletions src/main/kotlin/no/uib/echo/Application.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package no.uib.echo

import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.features.CORS
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.netty.EngineMain
import io.ktor.server.plugins.cors.routing.CORS
import no.uib.echo.plugins.configureRouting
import java.net.URI
import kotlin.Exception
Expand All @@ -23,42 +23,15 @@ fun main(args: Array<String>) {

fun Application.module() {
install(CORS) {
run {
method(HttpMethod.Get)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
allowNonSimpleContentTypes = true
}
run {
method(HttpMethod.Post)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
allowNonSimpleContentTypes = true
}
run {
method(HttpMethod.Put)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
allowNonSimpleContentTypes = true
}
run {
method(HttpMethod.Options)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
allowNonSimpleContentTypes = true
}
run {
method(HttpMethod.Patch)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
allowNonSimpleContentTypes = true
}
run {
method(HttpMethod.Delete)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
allowNonSimpleContentTypes = true
}
anyHost()

allowMethod(HttpMethod.Get)
allowMethod(HttpMethod.Put)
allowMethod(HttpMethod.Post)
allowMethod(HttpMethod.Delete)

allowHeader(HttpHeaders.ContentType)
allowHeader(HttpHeaders.Authorization)
}

val dev = environment.config.propertyOrNull("ktor.dev") != null
Expand Down
40 changes: 20 additions & 20 deletions src/main/kotlin/no/uib/echo/Email.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package no.uib.echo

import io.ktor.client.HttpClient
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.Logging
import io.ktor.client.request.headers
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.bearerAuth
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import no.uib.echo.plugins.Routing
import no.uib.echo.schema.HAPPENING_TYPE
import no.uib.echo.schema.Happening
import no.uib.echo.schema.Happening.registrationsLink
import no.uib.echo.schema.HappeningJson
import no.uib.echo.schema.RegistrationJson
import org.jetbrains.exposed.sql.StdOutSqlLogger
Expand Down Expand Up @@ -116,7 +116,7 @@ suspend fun sendConfirmationEmail(
}
}

suspend fun sendRegsLinkEmail(sendGridApiKey: String, happening: HappeningJson) {
suspend fun sendRegsLinkEmail(sendGridApiKey: String, happening: HappeningJson, regsLink: String) {
val hapTypeLiteral = when (happening.type) {
HAPPENING_TYPE.EVENT ->
"arrangementet"
Expand All @@ -131,7 +131,7 @@ suspend fun sendRegsLinkEmail(sendGridApiKey: String, happening: HappeningJson)
happening.organizerEmail,
SendGridTemplate(
happening.title,
"https://echo.uib.no/${Routing.registrationRoute}/$registrationsLink",
"https://echo.uib.no/${Routing.registrationRoute}/$regsLink",
hapTypeLiteral
),
Template.REGS_LINK,
Expand Down Expand Up @@ -175,14 +175,15 @@ suspend fun sendEmail(

val response: HttpResponse = HttpClient {
install(Logging)
install(JsonFeature) {
serializer = KotlinxSerializer()
install(ContentNegotiation) {
json()
}
}.use { client ->
client.post(SENDGRID_ENDPOINT) {
headers {
contentType(ContentType.Application.Json)
body = SendGridRequest(
contentType(ContentType.Application.Json)
bearerAuth(sendGridApiKey)
setBody(
SendGridRequest(
listOf(
SendGridPersonalization(
to = listOf(SendGridEmail(to)),
Expand All @@ -191,23 +192,22 @@ suspend fun sendEmail(
),
from = fromPers, template_id = templateId
)
append(HttpHeaders.Authorization, "Bearer $sendGridApiKey")
}
)
}
}

if (response.status != HttpStatusCode.Accepted) {
throw IOException("Status code is not 202: ${response.status}, ${response.content}")
throw IOException("Status code is not 202: ${response.status}, ${response.bodyAsText()}")
}
}

fun isEmailValid(email: String): Boolean {
return Pattern.compile(
"^(([\\w-]+\\.)+[\\w-]+|([a-zA-Z]|[\\w-]{2,}))@" +
"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\\.([0-1]?" +
"[0-9]{1,2}|25[0-5]|2[0-4][0-9])\\." +
"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\\.([0-1]?" +
"[0-9]{1,2}|25[0-5]|2[0-4][0-9]))|" +
"((([0-1]?\\d{1,2}|25[0-5]|2[0-4]\\d)\\.([0-1]?" +
"\\d{1,2}|25[0-5]|2[0-4]\\d)\\." +
"([0-1]?\\d{1,2}|25[0-5]|2[0-4]\\d)\\.([0-1]?" +
"\\d{1,2}|25[0-5]|2[0-4]\\d))|" +
"([a-zA-Z]+[\\w-]+\\.)+[a-zA-Z]{2,4})$"
).matcher(email).matches()
}
89 changes: 38 additions & 51 deletions src/main/kotlin/no/uib/echo/plugins/Routing.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
package no.uib.echo.plugins

import com.auth0.jwk.JwkProviderBuilder
import guru.zoroark.ratelimit.RateLimit
import guru.zoroark.ratelimit.rateLimited
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.auth.Authentication
import io.ktor.auth.UserIdPrincipal
import io.ktor.auth.authenticate
import io.ktor.auth.basic
import io.ktor.auth.jwt.JWTPrincipal
import io.ktor.auth.jwt.jwt
import io.ktor.auth.principal
import io.ktor.features.ContentNegotiation
import io.ktor.http.ContentDisposition
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.request.receive
import io.ktor.response.header
import io.ktor.response.respond
import io.ktor.response.respondBytes
import io.ktor.routing.Route
import io.ktor.routing.delete
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.routing
import io.ktor.serialization.json
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.auth.Authentication
import io.ktor.server.auth.UserIdPrincipal
import io.ktor.server.auth.authenticate
import io.ktor.server.auth.basic
import io.ktor.server.auth.jwt.JWTPrincipal
import io.ktor.server.auth.jwt.jwt
import io.ktor.server.auth.principal
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.request.receive
import io.ktor.server.response.header
import io.ktor.server.response.respond
import io.ktor.server.response.respondBytes
import io.ktor.server.routing.Route
import io.ktor.server.routing.delete
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.put
import io.ktor.server.routing.routing
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import no.uib.echo.FeatureToggles
Expand Down Expand Up @@ -81,7 +79,6 @@ import org.jetbrains.exposed.sql.update
import org.joda.time.DateTime
import java.net.URL
import java.net.URLDecoder
import java.time.Duration
import java.util.concurrent.TimeUnit

fun Application.configureRouting(
Expand All @@ -96,11 +93,6 @@ fun Application.configureRouting(
json()
}

install(RateLimit) {
limit = 200
timeBeforeReset = if (featureToggles.rateLimit) Duration.ofMinutes(2) else Duration.ZERO
}

install(Authentication) {
basic("auth-$admin") {
realm = "Access to registrations and happenings."
Expand Down Expand Up @@ -132,25 +124,23 @@ fun Application.configureRouting(
}

routing {
rateLimited {
getStatus()
getStatus()

authenticate("auth-$admin") {
putHappening(sendGridApiKey, featureToggles.sendEmailHap, dev)
deleteHappening()
getHappeningInfo()
}

authenticate("auth-jwt") {
getUser()
putUser()
}
authenticate("auth-$admin") {
putHappening(sendGridApiKey, featureToggles.sendEmailHap, dev)
deleteHappening()
getHappeningInfo()
}

getRegistrations(dev)
postRegistration(sendGridApiKey, featureToggles.sendEmailReg, featureToggles.verifyRegs)
deleteRegistration(dev)
postRegistrationCount()
authenticate("auth-jwt") {
getUser()
putUser()
}

getRegistrations(dev)
postRegistration(sendGridApiKey, featureToggles.sendEmailReg, featureToggles.verifyRegs)
deleteRegistration(dev)
postRegistrationCount()
}
}

Expand Down Expand Up @@ -285,7 +275,7 @@ object Routing {
HttpStatusCode.OK,
HappeningInfoJson(
registrationCount,
happening?.get(Happening.regVerifyToken)
happening[Happening.regVerifyToken]
)
)
}
Expand Down Expand Up @@ -532,7 +522,7 @@ object Routing {
it[degree] = registration.degree.toString()
it[degreeYear] = registration.degreeYear
it[happeningSlug] = registration.slug
it[terms] = registration.terms
it[terms] = true
it[Registration.waitList] = waitList
}

Expand Down Expand Up @@ -573,9 +563,6 @@ object Routing {

fun Route.deleteRegistration(dev: Boolean) {
delete("/$registrationRoute/{link}/{email}") {
fun stdResponse() {
}

val link = call.parameters["link"]
val email = withContext(Dispatchers.IO) {
URLDecoder.decode(call.parameters["email"], "utf-8")
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/no/uib/echo/schema/Happening.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ suspend fun insertOrUpdateHappening(
}

if (sendEmail && sendGridApiKey != null) {
sendRegsLinkEmail(sendGridApiKey, newHappening)
sendRegsLinkEmail(sendGridApiKey, newHappening, registrationsLink)
}

return Pair(
Expand Down Expand Up @@ -183,7 +183,7 @@ suspend fun insertOrUpdateHappening(
"and organizerEmail = ${newHappening.organizerEmail.lowercase()}."

if (happening[Happening.organizerEmail].lowercase() != newHappening.organizerEmail.lowercase() && sendEmail && sendGridApiKey != null) {
sendRegsLinkEmail(sendGridApiKey, newHappening)
sendRegsLinkEmail(sendGridApiKey, newHappening, registrationsLink)
return Pair(
HttpStatusCode.OK,
HappeningResponseJson(
Expand Down
Loading

0 comments on commit 55eda01

Please sign in to comment.