feature/swagger-oauth-env-config #101
@ -54,6 +54,7 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
runtimeOnly 'org.postgresql:postgresql'
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
implementation 'org.hibernate.validator:hibernate-validator'
|
implementation 'org.hibernate.validator:hibernate-validator'
|
||||||
|
runtimeOnly 'com.h2database:h2'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|||||||
import com.hithomelabs.CFTunnels.Config.AuthoritiesToGroupMapping;
|
import com.hithomelabs.CFTunnels.Config.AuthoritiesToGroupMapping;
|
||||||
import com.hithomelabs.CFTunnels.Config.CloudflareConfig;
|
import com.hithomelabs.CFTunnels.Config.CloudflareConfig;
|
||||||
import com.hithomelabs.CFTunnels.Config.RestTemplateConfig;
|
import com.hithomelabs.CFTunnels.Config.RestTemplateConfig;
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Request;
|
||||||
import com.hithomelabs.CFTunnels.Headers.AuthKeyEmailHeader;
|
import com.hithomelabs.CFTunnels.Headers.AuthKeyEmailHeader;
|
||||||
import com.hithomelabs.CFTunnels.Models.Config;
|
import com.hithomelabs.CFTunnels.Models.Config;
|
||||||
import com.hithomelabs.CFTunnels.Models.Ingress;
|
import com.hithomelabs.CFTunnels.Models.Ingress;
|
||||||
import com.hithomelabs.CFTunnels.Models.TunnelResponse;
|
import com.hithomelabs.CFTunnels.Models.TunnelResponse;
|
||||||
import com.hithomelabs.CFTunnels.Services.CloudflareAPIService;
|
import com.hithomelabs.CFTunnels.Services.CloudflareAPIService;
|
||||||
|
import com.hithomelabs.CFTunnels.Services.MappingRequestService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.*;
|
||||||
@ -44,6 +46,9 @@ public class TunnelController implements ErrorController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
CloudflareAPIService cloudflareAPIService;
|
CloudflareAPIService cloudflareAPIService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MappingRequestService mappingRequestService;
|
||||||
|
|
||||||
@PreAuthorize("hasAnyRole('USER')")
|
@PreAuthorize("hasAnyRole('USER')")
|
||||||
@GetMapping("/whoami")
|
@GetMapping("/whoami")
|
||||||
public Map<String,Object> whoAmI(@AuthenticationPrincipal OidcUser oidcUser) {
|
public Map<String,Object> whoAmI(@AuthenticationPrincipal OidcUser oidcUser) {
|
||||||
@ -132,4 +137,14 @@ public class TunnelController implements ErrorController {
|
|||||||
|
|
||||||
return ResponseEntity.ok(jsonResponse);
|
return ResponseEntity.ok(jsonResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAnyRole('DEVELOPER')")
|
||||||
|
@PutMapping("/tunnel/{tunnelId}/request")
|
||||||
|
public ResponseEntity<Request> createTunnelMappingRequest(@PathVariable String tunnelId, @AuthenticationPrincipal OidcUser oidcUser, @RequestBody Ingress ingess){
|
||||||
|
Request request = mappingRequestService.createMappingRequest(tunnelId, ingess, oidcUser);
|
||||||
|
if(request.getId() != null)
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(request);
|
||||||
|
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
34
src/main/java/com/hithomelabs/CFTunnels/Entity/Mapping.java
Normal file
34
src/main/java/com/hithomelabs/CFTunnels/Entity/Mapping.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "mappings")
|
||||||
|
public class Mapping {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
@Column(columnDefinition = "uuid", nullable = false, unique = true)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Column(length = 50, nullable = false)
|
||||||
|
private String subdomain;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "tunnel_id", nullable = false)
|
||||||
|
private Tunnel tunnel;
|
||||||
|
}
|
||||||
45
src/main/java/com/hithomelabs/CFTunnels/Entity/Request.java
Normal file
45
src/main/java/com/hithomelabs/CFTunnels/Entity/Request.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "requests")
|
||||||
|
public class Request {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
@Column(columnDefinition = "uuid", unique = true, nullable = false)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
@JoinColumn(name = "mapping_id", unique = true, nullable = false)
|
||||||
|
private Mapping mapping;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "created_by", nullable = false)
|
||||||
|
private User createdBy;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "accepted_by")
|
||||||
|
private User acceptedBy;
|
||||||
|
|
||||||
|
public enum RequestStatus {
|
||||||
|
PENDING,
|
||||||
|
APPROVED,
|
||||||
|
REJECTED
|
||||||
|
}
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 10, nullable = false)
|
||||||
|
private RequestStatus status;
|
||||||
|
}
|
||||||
29
src/main/java/com/hithomelabs/CFTunnels/Entity/Tunnel.java
Normal file
29
src/main/java/com/hithomelabs/CFTunnels/Entity/Tunnel.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name="tunnels")
|
||||||
|
public class Tunnel {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
@Column(columnDefinition = "uuid", insertable = false, updatable = false, nullable = false)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@Column(length = 10, unique = true, nullable = false)
|
||||||
|
private String environment;
|
||||||
|
|
||||||
|
@Column(name = "cf_tunnel_id", columnDefinition = "uuid", unique = true, nullable = false)
|
||||||
|
private UUID cfTunnelId;
|
||||||
|
}
|
||||||
30
src/main/java/com/hithomelabs/CFTunnels/Entity/User.java
Normal file
30
src/main/java/com/hithomelabs/CFTunnels/Entity/User.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Entity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Table(name = "users")
|
||||||
|
public class User {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
@Column(columnDefinition = "uuid", insertable = false, updatable = false, nullable = false)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@Column(length = 50, nullable = false)
|
||||||
|
@Size(max = 50)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(length = 50, nullable = false)
|
||||||
|
@Size(max = 50)
|
||||||
|
private String email;
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Repositories;
|
||||||
|
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Mapping;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface MappingRepository extends JpaRepository<Mapping, UUID> {
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Repositories;
|
||||||
|
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Request;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface RequestRepository extends JpaRepository<Request, UUID> {
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Repositories;
|
||||||
|
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Tunnel;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface TunnelRepository extends JpaRepository<Tunnel, UUID> {
|
||||||
|
Optional<Tunnel> findByCfTunnelId(UUID cfTunnelId);
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Repositories;
|
||||||
|
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.User;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UserRepository extends JpaRepository<User, UUID> {
|
||||||
|
|
||||||
|
Optional<User> findByEmail(String email);
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package com.hithomelabs.CFTunnels.Services;
|
||||||
|
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Mapping;
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Request;
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.Tunnel;
|
||||||
|
import com.hithomelabs.CFTunnels.Entity.User;
|
||||||
|
import com.hithomelabs.CFTunnels.Models.Ingress;
|
||||||
|
import com.hithomelabs.CFTunnels.Repositories.MappingRepository;
|
||||||
|
import com.hithomelabs.CFTunnels.Repositories.RequestRepository;
|
||||||
|
import com.hithomelabs.CFTunnels.Repositories.TunnelRepository;
|
||||||
|
import com.hithomelabs.CFTunnels.Repositories.UserRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MappingRequestService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
UserRepository userRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MappingRepository mappingRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RequestRepository requestRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TunnelRepository tunnelRepository;
|
||||||
|
|
||||||
|
public Mapping createMapping(UUID tunnelId, Ingress ingress){
|
||||||
|
Tunnel tunnel = tunnelRepository.findByCfTunnelId(tunnelId).orElseThrow(() -> new RuntimeException("Tunnel not found"));
|
||||||
|
Mapping mapping = createMappingFromTunnelIngress(tunnel, ingress);
|
||||||
|
return mappingRepository.save(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Request createRequest(Mapping mapping, User user){
|
||||||
|
Request request = new Request();
|
||||||
|
request.setMapping(mapping);
|
||||||
|
request.setCreatedBy(user);
|
||||||
|
request.setStatus(Request.RequestStatus.PENDING);
|
||||||
|
return requestRepository.save(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Request createMappingRequest(String tunnelId, Ingress ingress, OidcUser oidcUser){
|
||||||
|
User user = userRepository.findByEmail(oidcUser.getEmail()).orElseGet(()-> mapUser(oidcUser));
|
||||||
|
Mapping mapping = createMapping(UUID.fromString(tunnelId), ingress);
|
||||||
|
return createRequest(mapping, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User mapUser(OidcUser oidcUser){
|
||||||
|
String email = oidcUser.getEmail();
|
||||||
|
String name = oidcUser.getNickName();
|
||||||
|
User user = new User();
|
||||||
|
user.setEmail(email);
|
||||||
|
user.setName(name);
|
||||||
|
userRepository.save(user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mapping createMappingFromTunnelIngress(Tunnel tunnel, Ingress ingress){
|
||||||
|
Mapping mapping = new Mapping();
|
||||||
|
mapping.setTunnel(tunnel);
|
||||||
|
mapping.setPort(Integer.parseInt(ingress.getService().split(":")[2]));
|
||||||
|
mapping.setSubdomain(ingress.getHostname().split("\\.")[0]);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,7 @@
|
|||||||
api.baseUrl=https://testcf.hithomelabs.com
|
api.baseUrl=https://testcf.hithomelabs.com
|
||||||
|
|
||||||
|
spring.datasource.url: jdbc:h2:mem:testdb
|
||||||
|
spring.datasource.driver-class-name: org.h2.Driver
|
||||||
|
spring.datasource.username: sa
|
||||||
|
spring.datasource.password:
|
||||||
|
spring.datasource.jpa.hibernate.ddl-auto: none
|
||||||
@ -1,37 +1,29 @@
|
|||||||
-- schema.sql
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||||
|
|
||||||
-- Roles table
|
|
||||||
CREATE TABLE IF NOT EXISTS roles (
|
|
||||||
role_id SERIAL PRIMARY KEY,
|
|
||||||
role_name VARCHAR(50) UNIQUE NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Users table
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
user_id SERIAL PRIMARY KEY,
|
|
||||||
user_name VARCHAR(100) NOT NULL,
|
|
||||||
password VARCHAR(255) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- User-Role Mapping table (many-to-many relationship)
|
|
||||||
CREATE TABLE IF NOT EXISTS user_role_mapping (
|
|
||||||
mapping_id SERIAL PRIMARY KEY,
|
|
||||||
user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
|
|
||||||
role_id INTEGER NOT NULL REFERENCES roles(role_id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Tunnels table
|
|
||||||
CREATE TABLE IF NOT EXISTS tunnels (
|
CREATE TABLE IF NOT EXISTS tunnels (
|
||||||
tunnel_id SERIAL PRIMARY KEY,
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
tunnel_name VARCHAR(100) NOT NULL,
|
environment VARCHAR(10) NOT NULL,
|
||||||
tunnel_type VARCHAR(50) NOT NULL
|
cf_tunnel_id UUID UNIQUE NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Mapping Requests table
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
CREATE TABLE IF NOT EXISTS mapping_requests (
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
request_id SERIAL PRIMARY KEY,
|
name VARCHAR(50) NOT NULL
|
||||||
request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
);
|
||||||
status VARCHAR(20) NOT NULL,
|
|
||||||
user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
CREATE TABLE IF NOT EXISTS mappings (
|
||||||
tunnel_id INTEGER REFERENCES tunnels(tunnel_id) ON DELETE SET NULL
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tunnel_id UUID NOT NULL REFERENCES tunnels(id) ON DELETE CASCADE,
|
||||||
|
port INT NOT NULL,
|
||||||
|
subdomain VARCHAR(50) NOT NULL
|
||||||
|
-- UNIQUE (tunnel_id, port),
|
||||||
|
-- UNIQUE (tunnel_id, subdomain)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS requests (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
mapping_id UUID NOT NULL REFERENCES mappings(id) ON DELETE CASCADE,
|
||||||
|
created_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||||
|
accepted_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
status VARCHAR(20) NOT NULL CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED'))
|
||||||
);
|
);
|
||||||
@ -10,6 +10,7 @@ import com.hithomelabs.CFTunnels.Models.Config;
|
|||||||
import com.hithomelabs.CFTunnels.Models.Groups;
|
import com.hithomelabs.CFTunnels.Models.Groups;
|
||||||
import com.hithomelabs.CFTunnels.Models.TunnelResponse;
|
import com.hithomelabs.CFTunnels.Models.TunnelResponse;
|
||||||
import com.hithomelabs.CFTunnels.Services.CloudflareAPIService;
|
import com.hithomelabs.CFTunnels.Services.CloudflareAPIService;
|
||||||
|
import com.hithomelabs.CFTunnels.Services.MappingRequestService;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -66,6 +67,9 @@ class TunnelControllerTest {
|
|||||||
@MockitoBean
|
@MockitoBean
|
||||||
RestTemplateConfig restTemplateConfig;
|
RestTemplateConfig restTemplateConfig;
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
MappingRequestService mappingRequestService;
|
||||||
|
|
||||||
private static final String tunnelResponseSmallIngressFile = "tunnelResponseSmallIngress.json";
|
private static final String tunnelResponseSmallIngressFile = "tunnelResponseSmallIngress.json";
|
||||||
|
|
||||||
private static final String tunnelResponseLargeIngressFile = "tunnelResponseLargeIngress.json";
|
private static final String tunnelResponseLargeIngressFile = "tunnelResponseLargeIngress.json";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user