Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into discord
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Czeladka committed Sep 14, 2023
2 parents 82ee6b3 + e2a042a commit c3c99bc
Show file tree
Hide file tree
Showing 75 changed files with 4,885 additions and 6,646 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [0.2.35](https://github.com/cardano-foundation/cf-cardano-ballot/compare/v0.2.34...v0.2.35) (2023-09-14)


### Bug Fixes

* dummy commit to force release-please ([34ec8ca](https://github.com/cardano-foundation/cf-cardano-ballot/commit/34ec8ca5639f7c57530f37b316311f5b486ebf96))

## [0.2.34](https://github.com/cardano-foundation/cf-cardano-ballot/compare/v0.2.33...v0.2.34) (2023-09-13)


### Features

* web3 spring security ([#188](https://github.com/cardano-foundation/cf-cardano-ballot/issues/188)) ([a81e735](https://github.com/cardano-foundation/cf-cardano-ballot/commit/a81e7359e668b6e34e8314a885e7b8e3a5000053))

## [0.2.33](https://github.com/cardano-foundation/cf-cardano-ballot/compare/v0.2.32...v0.2.33) (2023-09-13)


### Bug Fixes

* removed the unused component ([753da52](https://github.com/cardano-foundation/cf-cardano-ballot/commit/753da52098b98d2282205433c25a5b051f3b30e5))

## [0.2.32](https://github.com/cardano-foundation/cf-cardano-ballot/compare/v0.2.31...v0.2.32) (2023-09-13)


Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ A set of backend services and UI applications to facilitate CIP-1694 voting as w
[![UI-App-Build](https://github.com/cardano-foundation/cf-cardano-ballot/actions/workflows/ui-cypress-tests.yaml/badge.svg)](https://github.com/cardano-foundation/cf-cardano-ballot/actions/workflows/ui-cypress-tests.yaml)

## Requirements

- Node.js 18.x LTS
- Java 17 LTS
- Postgres DB 14.x or H2 file db (local development / community running).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cardano.foundation.voting.config;

import org.cardano.foundation.voting.service.auth.JwtFilter;
import org.cardano.foundation.voting.service.auth.jwt.JwtFilter;
import org.cardano.foundation.voting.service.auth.web3.Web3Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
Expand All @@ -15,6 +16,7 @@
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;

import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;

@Configuration
Expand All @@ -28,6 +30,9 @@ public class SpringSecurityConfiguration {
@Autowired
private JwtFilter jwtFilter;

@Autowired
private Web3Filter web3Filter;

@ConditionalOnProperty( //to make sure it is active if console is enabled
value="spring.h2.console.enabled",
havingValue = "true",
Expand All @@ -42,7 +47,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
// Add a filter to validate the tokens with every request

.addFilterBefore(web3Filter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)

.sessionManagement()
Expand All @@ -58,12 +64,51 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

.and()

// SECURED by Web3 auth
.authorizeHttpRequests()
.anyRequest()
.requestMatchers(GET, "/api/vote/receipt")
.authenticated()

.and()

// SECURED by Web3 auth
.authorizeHttpRequests()
.requestMatchers(GET, "/api/auth/login")
.authenticated()

.and()

// SECURED by Web3 auth
.authorizeHttpRequests()
.requestMatchers(POST, "/api/vote/cast")
.authenticated()

.and()

.authorizeHttpRequests()
.requestMatchers(GET, "/api/leaderboard/**")
.permitAll()

.and()

.authorizeHttpRequests()
.requestMatchers(GET, "/actuator/**")
.permitAll()

.and()

.authorizeHttpRequests()
.requestMatchers("/h2-console")
.permitAll()

.and()

.authorizeHttpRequests()
.anyRequest()
.denyAll()

.and()

.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import lombok.Builder;
import lombok.Getter;
import org.cardano.foundation.voting.utils.Enums;

import java.util.Optional;

@Builder
@Getter
Expand All @@ -13,4 +16,12 @@ public class CIP93Envelope<T> {
private String slot;
private T data;

public long getSlotAsLong() {
return Long.parseLong(slot);
}

public Optional<Web3Action> getActionAsEnum() {
return Enums.getIfPresent(Web3Action.class, action);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.cardano.foundation.voting.domain.web3;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -10,7 +9,7 @@
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class LoginEnvelope {
public class JwtLoginEnvelope {

private String event;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class ViewVoteReceiptEnvelope {
private String network;

private String event;

private String category;

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ public class VoteEnvelope {
@Builder.Default
private Optional<String> votingPower = Optional.empty(); // voting power is only available for STAKE_BASED or BALANCE_BASED events

public long getVotedAtSlot() {
return Long.parseLong(votedAt);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.cardano.foundation.voting.resource;

public final class Headers {

public final static String XCIP93Signature = "X-CIP93-Signature";
public final static String XCIP93PublicKey = "X-CIP93-Public-Key";
public final static String XForceLeaderBoardResults = "X-Force-Leaderboard-Results";

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.HEAD;

import static org.cardano.foundation.voting.resource.Headers.XForceLeaderBoardResults;

@RestController
@RequestMapping("/api/leaderboard")
@Slf4j
Expand All @@ -29,34 +31,34 @@ public class LeaderboardResource {
@RequestMapping(value = "/event/{eventId}/", method = HEAD, produces = "application/json")
@Timed(value = "resource.leaderboard.high.level.event.available", histogram = true)
public ResponseEntity<?> isHighLevelEventLeaderBoardAvailable(@PathVariable("eventId") String eventId,
@RequestHeader(value = "force-leaderboard-results", required = false, defaultValue = "false") boolean forceLeaderboardResults) {
@RequestHeader(value = XForceLeaderBoardResults, required = false, defaultValue = "false") boolean forceLeaderboardResults) {
var forceLeaderboard = forceLeaderboardResults && forceLeaderboardResultsAvailability;

var availableE = leaderBoardService.isHighLevelEventLeaderboardAvailable(eventId, forceLeaderboard);

return availableE.fold(problem -> ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem),
isAvailable -> isAvailable ? ResponseEntity.ok().build() : ResponseEntity.status(FORBIDDEN).build()
);
isAvailable -> isAvailable ? ResponseEntity.ok().build() : ResponseEntity.status(FORBIDDEN).build()
);
}

@RequestMapping(value = "/event-category/{eventId}", method = HEAD, produces = "application/json")
@Timed(value = "resource.leaderboard.high.level.category.available", histogram = true)
public ResponseEntity<?> isHighLevelCategoryLeaderBoardAvailable(@PathVariable("eventId") String eventId,
@RequestHeader(value = "force-leaderboard-results", required = false, defaultValue = "false") boolean forceLeaderboardResults) {
@RequestHeader(value = XForceLeaderBoardResults, required = false, defaultValue = "false") boolean forceLeaderboardResults) {
var forceLeaderboard = forceLeaderboardResults && forceLeaderboardResultsAvailability;

var availableE = leaderBoardService.isHighLevelCategoryLeaderboardAvailable(eventId, forceLeaderboard);

return availableE.fold(problem -> ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem),
isAvailable -> isAvailable ? ResponseEntity.ok().build() : ResponseEntity.status(FORBIDDEN).build()
);
isAvailable -> isAvailable ? ResponseEntity.ok().build() : ResponseEntity.status(FORBIDDEN).build()
);
}

@RequestMapping(value = "/{eventId}/{categoryId}", method = HEAD, produces = "application/json")
@Timed(value = "resource.leaderboard.category.available", histogram = true)
public ResponseEntity<?> getCategoryLeaderBoardAvailable(@PathVariable("eventId") String eventId,
@PathVariable("categoryId") String categoryId,
@RequestHeader(value = "force-leaderboard-results", required = false, defaultValue = "false") boolean forceLeaderboardResults) {
@RequestHeader(value = XForceLeaderBoardResults, required = false, defaultValue = "false") boolean forceLeaderboardResults) {
var forceLeaderboard = forceLeaderboardResults && forceLeaderboardResultsAvailability;

var categoryLeaderboardAvailableE = leaderBoardService.isCategoryLeaderboardAvailable(eventId, categoryId, forceLeaderboard);
Expand All @@ -70,21 +72,21 @@ public ResponseEntity<?> getCategoryLeaderBoardAvailable(@PathVariable("eventId"
@RequestMapping(value = "/{eventId}", method = GET, produces = "application/json")
@Timed(value = "resource.leaderboard.event", histogram = true)
public ResponseEntity<?> getEventLeaderBoard(@PathVariable("eventId") String eventId,
@RequestHeader(value = "force-leaderboard-results", required = false, defaultValue = "false") boolean forceLeaderboardResults) {
@RequestHeader(value = XForceLeaderBoardResults, required = false, defaultValue = "false") boolean forceLeaderboardResults) {
var forceLeaderboard = forceLeaderboardResults && forceLeaderboardResultsAvailability;

var eventLeaderboardE = leaderBoardService.getEventLeaderboard(eventId, forceLeaderboard);

return eventLeaderboardE.fold(problem -> ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem),
response -> ResponseEntity.ok().body(response)
);
response -> ResponseEntity.ok().body(response)
);
}

@RequestMapping(value = "/{eventId}/{categoryId}", method = GET, produces = "application/json")
@Timed(value = "resource.leaderboard.category", histogram = true)
public ResponseEntity<?> getCategoryLeaderBoard(@PathVariable("eventId") String eventId,
@PathVariable("categoryId") String categoryId,
@RequestHeader(value = "force-leaderboard-results", required = false, defaultValue = "false") boolean forceLeaderboardResults) {
@RequestHeader(value = XForceLeaderBoardResults, required = false, defaultValue = "false") boolean forceLeaderboardResults) {
var forceLeaderboard = forceLeaderboardResults && forceLeaderboardResultsAvailability;

var categoryLeaderboardE = leaderBoardService.getCategoryLeaderboard(eventId, categoryId, forceLeaderboard);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package org.cardano.foundation.voting.resource;

import io.micrometer.core.annotation.Timed;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.domain.web3.SignedWeb3Request;
import org.cardano.foundation.voting.service.auth.LoginService;
import org.cardano.foundation.voting.service.auth.web3.Web3AuthenticationToken;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;

import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

@RestController
Expand All @@ -23,14 +23,12 @@ public class LoginResource {

private final LoginService loginService;

@RequestMapping(value = "/login", method = POST, produces = "application/json")
@RequestMapping(value = "/login", method = GET, produces = "application/json")
@Timed(value = "resource.auth.login", histogram = true)
public ResponseEntity<?> login(@RequestBody @Valid SignedWeb3Request loginRequest) {
return loginService.login(loginRequest)
.fold(problem -> {
return ResponseEntity.status(Objects.requireNonNull(problem.getStatus()).getStatusCode()).body(problem);
},
loginResult -> ResponseEntity.ok(loginResult)
public ResponseEntity<?> login(Authentication authentication) {
return loginService.login((Web3AuthenticationToken) authentication)
.fold(problem -> ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem),
ResponseEntity::ok
);
}

Expand Down
Loading

0 comments on commit c3c99bc

Please sign in to comment.