Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/open-api-ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ jobs:
-e "OPEN_TOKEN_ADDRESS=${{ secrets.OPEN_TOKEN_ADDRESS_PROD }}" \
-e "EVENT_SUBSCRIPTION=true" \
-e "WIDGET_HOST=${{ secrets.WIDGET_HOST_PROD }}" \
-e "OPEN_STATE_URL=${{ secrets.OPEN_STATE_URLPROD }}" \
-e "OPEN_STATE_URL=${{ secrets.OPEN_STATE_URL_PROD }}" \
-e "STATE_API_URL=${{ secrets.STATE_API_URL_PROD }}" \
-e "OPEN_KEY_URL=${{ secrets.OPEN_KEY_URL_PROD }}" \
${{ env.DEPLOY_IMAGE_NAME }}:${{ env.DEPLOY_IMAGE_TAG }}
"

Expand Down
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ dependencies {
// Utils
implementation('commons-io:commons-io:2.8.0')
implementation 'org.apache.commons:commons-lang3:3.6'
implementation group: 'commons-net', name: 'commons-net', version: '3.6'


// Test
testImplementation('org.springframework.boot:spring-boot-starter-test')
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/GatewayApplicationRemove.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {t} from "../utils/messageTexts";
import React from "react";

export const GatewayApplicationRemove = ({ onSubmit }) => (
<EntityRemove onSubmit={onSubmit} header="Delete Gateway">
<EntityRemove onSubmit={onSubmit} header="Delete Application">
<div>
{t('sure to delete Gateway')}
{t('sure to delete Application')}
</div>
</EntityRemove>
);
);
3 changes: 2 additions & 1 deletion src/main/kotlin/io/openfuture/api/config/SecurityConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ class SecurityConfig(
.antMatchers("/static/**").permitAll()
.antMatchers("**.js").permitAll()
.antMatchers("/widget/**").permitAll()
.antMatchers("/**").access("hasIpAddress('127.0.0.1') or hasIpAddress('0:0:0:0:0:0:0:1') or hasIpAddress('${properties.cidr}')")
.anyRequest().authenticated()

.and()

.addFilterAfter(AuthorizationFilter(properties, keyService), OAuth2LoginAuthenticationFilter::class.java)
.addFilterAfter(ApiAuthorizationFilter(mapper), AuthorizationFilter::class.java)
.addFilterAfter(ApiAuthorizationFilter(mapper,properties), AuthorizationFilter::class.java)
.addFilterAfter(PublicApiAuthorizationFilter(applicationService, mapper, properties), AuthorizationFilter::class.java)
.sessionManagement().sessionCreationPolicy(STATELESS)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package io.openfuture.api.config.filter

import com.fasterxml.jackson.databind.ObjectMapper
import io.openfuture.api.config.propety.AuthorizationProperties
import io.openfuture.api.domain.exception.ExceptionResponse
import org.springframework.http.HttpStatus.UNAUTHORIZED
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.util.matcher.IpAddressMatcher
import javax.servlet.*
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class ApiAuthorizationFilter(private val mapper: ObjectMapper): Filter {
class ApiAuthorizationFilter(
private val mapper: ObjectMapper,
private val properties: AuthorizationProperties
): Filter {

private val ipV4LoopBack = "127.0.0.1"
private val ipV6LoopBack = "0:0:0:0:0:0:0:1"

override fun init(filterConfig: FilterConfig?) {
// Do nothing
Expand All @@ -18,7 +26,7 @@ class ApiAuthorizationFilter(private val mapper: ObjectMapper): Filter {
request as HttpServletRequest
response as HttpServletResponse

if (request.requestURI.startsWith("/api") && null == SecurityContextHolder.getContext().authentication) {
if (request.requestURI.startsWith("/api") && null == SecurityContextHolder.getContext().authentication && !isAllowed(request)) {
val exceptionResponse = ExceptionResponse(UNAUTHORIZED.value(), "Open token is invalid or disabled")
response.status = exceptionResponse.status
response.writer.write(mapper.writeValueAsString(exceptionResponse))
Expand All @@ -32,4 +40,22 @@ class ApiAuthorizationFilter(private val mapper: ObjectMapper): Filter {
// Do nothing
}

fun isAllowed(request: HttpServletRequest): Boolean {

val ip = request.remoteAddr
if (properties.allowLocalHost && (ipV4LoopBack == ip || ipV6LoopBack == ip)) {
return true
}

if (properties.cidr != null) {
val matcher = IpAddressMatcher(properties.cidr)

if (matcher.matches(request.getHeader("X-Forwarded-For"))) {
return true
}
}

return false
}

}
72 changes: 72 additions & 0 deletions src/main/kotlin/io/openfuture/api/config/filter/IpAddressFilter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.openfuture.api.config.filter

import io.openfuture.api.config.propety.AuthorizationProperties
import io.openfuture.api.util.getIpRange
import org.springframework.security.web.util.matcher.IpAddressMatcher
import java.io.IOException
import javax.servlet.*
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse


class IpAddressFilter(
private val properties: AuthorizationProperties
) : Filter {

private val IPV4_LOOPBACK = "127.0.0.1"
private val IPV6_LOOPBACK = "0:0:0:0:0:0:0:1"
private var ipList = arrayListOf<String>()
var allowLocalhost = true

override fun init(filterConfig: FilterConfig?) {
ipList = getIpRange(properties.cidr!!)
ipList.stream().map { ip -> print(ip) }
}

override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
request as HttpServletRequest
response as HttpServletResponse

println("REMOTE ADDRESS ${request.getHeader("X-Forwarded-For")}")


if (!isAllowed(request)) {
println("DENIED")
deny(response)
return
}
chain.doFilter(request, response)
}

@Throws(IOException::class)
fun deny(res: HttpServletResponse) {
res.sendError(HttpServletResponse.SC_NOT_FOUND)
}

override fun destroy() {

}

fun isAllowed(request: HttpServletRequest): Boolean {

val ip = request.remoteAddr
if (allowLocalhost && (IPV4_LOOPBACK == ip || IPV6_LOOPBACK == ip)) {
return true
}
/*var uri = request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE) as String
if (!StringUtils.isEmpty(uri)) {
uri = request.requestURI
if (request.contextPath != "/" && uri.startsWith(request.contextPath)) {
uri = uri.substring(request.contextPath.length)
}
}*/

val matcher = IpAddressMatcher("192.168.1.0/24")

if (!matcher.matches(request.getHeader("X-Forwarded-For"))) {
return true
}

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import org.springframework.http.HttpStatus.UNAUTHORIZED
import io.openfuture.api.domain.exception.ExceptionResponse
import io.openfuture.api.domain.key.WalletApiCreateRequest
import io.openfuture.api.service.ApplicationService
import io.openfuture.api.util.CustomHttpRequestWrapper
import io.openfuture.api.util.KeyGeneratorUtils
import io.openfuture.api.util.currentEpochs
import io.openfuture.api.util.differenceEpochs
import io.openfuture.api.util.*
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.context.SecurityContextHolder
Expand All @@ -36,7 +33,7 @@ class PublicApiAuthorizationFilter(

val accessKey = request.getHeader("X-API-KEY")
val signature = request.getHeader("X-API-SIGNATURE")
val expirePeriod = 10L;
val expirePeriod = properties.expireApi!!

val requestWrapper = CustomHttpRequestWrapper(request)
val walletApiCreateRequest = mapper.readValue(requestWrapper.bodyInStringFormat, WalletApiCreateRequest::class.java)
Expand All @@ -59,7 +56,7 @@ class PublicApiAuthorizationFilter(
val token = UsernamePasswordAuthenticationToken(application.user, null, listOf(SimpleGrantedAuthority("ROLE_APPLICATION")))
SecurityContextHolder.getContext().authentication = token

chain.doFilter(requestWrapper, response);
chain.doFilter(requestWrapper, response)
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ import javax.validation.constraints.NotEmpty
@Component
class AuthorizationProperties(
@field:NotEmpty var cookieName: String? = null,
var expireApi: Long? = 10
var expireApi: Long? = 10,
var cidr: String? = null,
var allowLocalHost: Boolean = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ApplicationApiController(
}

@GetMapping("/{id}")
fun get(@CurrentUser user: User, @PathVariable id: Long): ApplicationDto {
fun get(@PathVariable id: Long): ApplicationDto {
val application = service.getById(id)
return ApplicationDto(application)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DefaultApplicationWalletService(
val keyWalletDto = keyApi.generateKey(CreateKeyRequest(request.applicationId, user.id.toString(), request.blockchainType))

// Save webhook on state
request.webHook.let { stateApi.createWallet(keyWalletDto.address, it, Blockchain.Ethereum) }
//request.webHook.let { stateApi.createWallet(keyWalletDto.address, it, Blockchain.Ethereum) }

return keyWalletDto
}
Expand All @@ -35,6 +35,6 @@ class DefaultApplicationWalletService(
// Delete from Open Key
keyApi.deleteAllKeysByApplicationAddress(applicationId, address)
// Delete from Open State
stateApi.deleteWallet(address, Blockchain.Ethereum)
//stateApi.deleteWallet(address, Blockchain.Ethereum)
}
}
9 changes: 9 additions & 0 deletions src/main/kotlin/io/openfuture/api/util/NetworkUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.openfuture.api.util

import org.apache.commons.net.util.SubnetUtils

fun getIpRange(subnet: String): ArrayList<String> {
val utils = SubnetUtils(subnet)

return utils.info.allAddresses!!.toCollection(ArrayList())
}
27 changes: 27 additions & 0 deletions src/main/resources/application-local.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SECURITY
spring.security.oauth2.client.registration.google.clientId=${GOOGLE_CLIENT_ID}
spring.security.oauth2.client.registration.google.clientSecret=${GOOGLE_CLIENT_SECRET}

# DATABASE
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST}/${POSTGRES_DB}
spring.datasource.username=${POSTGRES_USER}
spring.datasource.password=${POSTGRES_PASSWORD}
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database=postgresql
spring.flyway.out-of-order=true

# WEB3
web3j.client-address=${NETWORK_URL}
ethereum.private-key=${ETHEREUM_PRIVATE_KEY}
ethereum.open-token-address=${OPEN_TOKEN_ADDRESS}
ethereum.event-subscription=${EVENT_SUBSCRIPTION}

# AUTH
auth.cookie-name=open_key

# WIDGET
widget.host=${WIDGET_HOST}

# STATE
state.base-url=${OPEN_STATE_URL}
2 changes: 2 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ ethereum.event-subscription=${EVENT_SUBSCRIPTION}
# AUTH
auth.cookie-name=open_key
auth.expire-api=10
auth.cidr=${PUBLIC_IP_SUBNET}
auth.allow-local-host=false

# WIDGET
widget.host=${WIDGET_HOST}
Expand Down