db integration

This commit is contained in:
= 2026-01-04 23:25:48 +05:30
parent 4075eb78c8
commit 0cd1a827ab
13 changed files with 315 additions and 40 deletions

View File

@ -1,5 +1,7 @@
package com.hithomelabs.CFTunnels.Config; package com.hithomelabs.CFTunnels.Config;
import com.hithomelabs.CFTunnels.Entity.User;
import com.hithomelabs.CFTunnels.Repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
@ -18,6 +20,9 @@ public class CustomOidcUserConfiguration extends OidcUserService {
@Autowired @Autowired
AuthoritiesToGroupMapping authoritiesToGroupMapping; AuthoritiesToGroupMapping authoritiesToGroupMapping;
@Autowired
UserRepository userRepository;
@Override @Override
public OidcUser loadUser(OidcUserRequest userRequest) { public OidcUser loadUser(OidcUserRequest userRequest) {
// * * Delegate to the default implementation for loading user and claims // * * Delegate to the default implementation for loading user and claims
@ -34,6 +39,16 @@ public class CustomOidcUserConfiguration extends OidcUserService {
); );
} }
String email = oidcUser.getEmail();
String name = oidcUser.getNickName();
userRepository.findByEmail(email).orElseGet(() -> {
User user = new User();
user.setEmail(email);
user.setName(name);
return userRepository.save(user);
});
// * * Return a new DefaultOidcUser with merged authorities // * * Return a new DefaultOidcUser with merged authorities
return new DefaultOidcUser( return new DefaultOidcUser(
mappedAuthorities, mappedAuthorities,

View File

@ -4,24 +4,26 @@ 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.User;
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.Repositories.UserRepository;
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.*;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
@RestController @RestController
@RequestMapping("/cloudflare") @RequestMapping("/cloudflare")
@ -44,6 +46,12 @@ public class TunnelController implements ErrorController {
@Autowired @Autowired
CloudflareAPIService cloudflareAPIService; CloudflareAPIService cloudflareAPIService;
@Autowired
UserRepository userRepository;
@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 +140,14 @@ public class TunnelController implements ErrorController {
return ResponseEntity.ok(jsonResponse); return ResponseEntity.ok(jsonResponse);
} }
@PreAuthorize("hasAnyRole('DEVELOPER')")
@PutMapping("/tunnel/{tunnelId}/request")
public ResponseEntity<Ingress> createTunnelMappingRequest(@PathVariable UUID tunnelId, @AuthenticationPrincipal OidcUser oidcUser, @RequestBody Ingress ingess){
String email = oidcUser.getEmail();
User user = userRepository.findByEmail(email).orElseThrow(() -> new RuntimeException("User Not Found: " + email));
mappingRequestService.createMappingRequest(tunnelId, ingess, user);
return ResponseEntity.ok(ingess);
}
} }

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View File

@ -1,5 +1,6 @@
package com.hithomelabs.CFTunnels.Models; package com.hithomelabs.CFTunnels.Models;
import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -46,4 +47,13 @@ public class Ingress {
public void setOriginRequest(Map<String, Object> originRequest) { public void setOriginRequest(Map<String, Object> originRequest) {
this.originRequest = originRequest; this.originRequest = originRequest;
} }
public int getPort(){
URI url = URI.create(this.service);
return url.getPort();
}
public String getSubdomain(){
return this.hostname.split("//")[0];
}
} }

View File

@ -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> {
}

View File

@ -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> {
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1,53 @@
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.stereotype.Service;
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 = new Mapping();
mapping.setTunnel(tunnel);
mapping.setPort(ingress.getPort());
mapping.setSubdomain(ingress.getSubdomain());
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(UUID tunnelId, Ingress ingress, User user){
Mapping mapping = createMapping(tunnelId, ingress);
return createRequest(mapping, user);
}
}

View File

@ -1,37 +1,29 @@
-- schema.sql --CREATE EXTENSION IF NOT EXISTS "pgcrypto";
--
-- Roles table --CREATE TABLE IF NOT EXISTS tunnels (
CREATE TABLE IF NOT EXISTS roles ( -- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
role_id SERIAL PRIMARY KEY, -- environment VARCHAR(10) NOT NULL,
role_name VARCHAR(50) UNIQUE NOT NULL -- cf_tunnel_id UUID UNIQUE NOT NULL
); --);
--
-- Users table --CREATE TABLE IF NOT EXISTS users (
CREATE TABLE IF NOT EXISTS users ( -- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id SERIAL PRIMARY KEY, -- name VARCHAR(50) NOT NULL
user_name VARCHAR(100) NOT NULL, --);
password VARCHAR(255) NOT NULL --
); --CREATE TABLE IF NOT EXISTS mappings (
-- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- User-Role Mapping table (many-to-many relationship) -- tunnel_id UUID NOT NULL REFERENCES tunnels(id) ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS user_role_mapping ( -- port INT NOT NULL,
mapping_id SERIAL PRIMARY KEY, -- subdomain VARCHAR(50) NOT NULL
user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, ---- UNIQUE (tunnel_id, port),
role_id INTEGER NOT NULL REFERENCES roles(role_id) ON DELETE CASCADE ---- UNIQUE (tunnel_id, subdomain)
); --);
--
-- Tunnels table --CREATE TABLE IF NOT EXISTS requests (
CREATE TABLE IF NOT EXISTS tunnels ( -- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tunnel_id SERIAL PRIMARY KEY, -- mapping_id UUID NOT NULL REFERENCES mappings(id) ON DELETE CASCADE,
tunnel_name VARCHAR(100) NOT NULL, -- created_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
tunnel_type VARCHAR(50) NOT NULL -- accepted_by UUID REFERENCES users(id) ON DELETE SET NULL,
); -- status VARCHAR(20) NOT NULL CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED'))
--);
-- Mapping Requests table
CREATE TABLE IF NOT EXISTS mapping_requests (
request_id SERIAL PRIMARY KEY,
request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) NOT NULL,
user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
tunnel_id INTEGER REFERENCES tunnels(tunnel_id) ON DELETE SET NULL
);