This commit is contained in:
parent
25a37ba90d
commit
4fef573447
|
@ -33,7 +33,7 @@ COPY Makefile ./
|
||||||
|
|
||||||
# Build the frontend
|
# Build the frontend
|
||||||
COPY frontend frontend
|
COPY frontend frontend
|
||||||
RUN VITE_API_URL=/api npm run --prefix frontend build
|
RUN VITE_API_URL=/api VITE_OIDC_URL=/oidc npm run --prefix frontend build
|
||||||
|
|
||||||
# Build backend
|
# Build backend
|
||||||
COPY goshort.go ./
|
COPY goshort.go ./
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -9,7 +9,7 @@ install-backend:
|
||||||
install: install-frontend install-backend
|
install: install-frontend install-backend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
VITE_API_URL=/api npm run --prefix frontend build
|
VITE_API_URL=/api VITE_OIDC_URL=/oidc npm run --prefix frontend build
|
||||||
|
|
||||||
VERSION=$(shell git describe --tags --abbrev=0)
|
VERSION=$(shell git describe --tags --abbrev=0)
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ dev:
|
||||||
go run goshort.go dev
|
go run goshort.go dev
|
||||||
|
|
||||||
lint-frontend:
|
lint-frontend:
|
||||||
VITE_API_URL=/api npm run --prefix frontend lint
|
VITE_API_URL=/api VITE_OIDC_URL=/oidc npm run --prefix frontend lint
|
||||||
|
|
||||||
lint-frontend-fix:
|
lint-frontend-fix:
|
||||||
VITE_API_URL=/api npm run --prefix frontend lint:fix
|
VITE_API_URL=/api VITE_OIDC_URL=/oidc npm run --prefix frontend lint:fix
|
||||||
|
|
||||||
lint-backend:
|
lint-backend:
|
||||||
golangci-lint run
|
golangci-lint run
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
oidcserver "git.maronato.dev/maronato/goshort/internal/server/oidc"
|
oidcserver "git.maronato.dev/maronato/goshort/internal/server/oidc"
|
||||||
shortserver "git.maronato.dev/maronato/goshort/internal/server/short"
|
shortserver "git.maronato.dev/maronato/goshort/internal/server/short"
|
||||||
staticssterver "git.maronato.dev/maronato/goshort/internal/server/static"
|
staticssterver "git.maronato.dev/maronato/goshort/internal/server/static"
|
||||||
|
configservice "git.maronato.dev/maronato/goshort/internal/service/config"
|
||||||
oidcservice "git.maronato.dev/maronato/goshort/internal/service/oidc"
|
oidcservice "git.maronato.dev/maronato/goshort/internal/service/oidc"
|
||||||
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
||||||
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
||||||
|
@ -105,17 +106,18 @@ func serveAPI(ctx context.Context, cfg *config.Config) error {
|
||||||
tokenService := tokenservice.NewTokenService(storage)
|
tokenService := tokenservice.NewTokenService(storage)
|
||||||
shortLogService := shortlogservice.NewShortLogService(storage)
|
shortLogService := shortlogservice.NewShortLogService(storage)
|
||||||
oidcService := oidcservice.NewOIDCService(ctx, cfg)
|
oidcService := oidcservice.NewOIDCService(ctx, cfg)
|
||||||
|
configService := configservice.NewConfigService(cfg)
|
||||||
|
|
||||||
// Start short log worker
|
// Start short log worker
|
||||||
stopWorker, _ := shortLogService.StartWorker(ctx)
|
stopWorker, _ := shortLogService.StartWorker(ctx)
|
||||||
defer stopWorker()
|
defer stopWorker()
|
||||||
|
|
||||||
// Create handlers
|
// Create handlers
|
||||||
apiHandler := apiserver.NewAPIHandler(shortService, userService, tokenService, shortLogService)
|
apiHandler := apiserver.NewAPIHandler(shortService, userService, tokenService, shortLogService, configService)
|
||||||
shortHandler := shortserver.NewShortHandler(shortService, shortLogService)
|
shortHandler := shortserver.NewShortHandler(shortService, shortLogService)
|
||||||
healthcheckHandler := healthcheckserver.NewHealthcheckHandler(storage)
|
healthcheckHandler := healthcheckserver.NewHealthcheckHandler(storage)
|
||||||
docsHandler := staticssterver.NewStaticHandler(cfg, "/api/docs", docs.Assets())
|
docsHandler := staticssterver.NewStaticHandler(cfg, "/api/docs", docs.Assets())
|
||||||
oidcHandler := oidcserver.NewOIDCHandler(oidcService, userService)
|
oidcHandler := oidcserver.NewOIDCHandler(cfg, oidcService, userService)
|
||||||
|
|
||||||
// Create routers
|
// Create routers
|
||||||
apiRouter := apiserver.NewAPIRouter(apiHandler)
|
apiRouter := apiserver.NewAPIRouter(apiHandler)
|
||||||
|
@ -146,6 +148,7 @@ func serveAPI(ctx context.Context, cfg *config.Config) error {
|
||||||
if oidcService != nil {
|
if oidcService != nil {
|
||||||
srv.Mux.Mount("/oidc", oidcRouter)
|
srv.Mux.Mount("/oidc", oidcRouter)
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.Mux.Mount("/healthz", healthcheckRouter)
|
srv.Mux.Mount("/healthz", healthcheckRouter)
|
||||||
srv.Mux.Mount("/", chainedRouter)
|
srv.Mux.Mount("/", chainedRouter)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
oidcserver "git.maronato.dev/maronato/goshort/internal/server/oidc"
|
oidcserver "git.maronato.dev/maronato/goshort/internal/server/oidc"
|
||||||
shortserver "git.maronato.dev/maronato/goshort/internal/server/short"
|
shortserver "git.maronato.dev/maronato/goshort/internal/server/short"
|
||||||
staticssterver "git.maronato.dev/maronato/goshort/internal/server/static"
|
staticssterver "git.maronato.dev/maronato/goshort/internal/server/static"
|
||||||
|
configservice "git.maronato.dev/maronato/goshort/internal/service/config"
|
||||||
oidcservice "git.maronato.dev/maronato/goshort/internal/service/oidc"
|
oidcservice "git.maronato.dev/maronato/goshort/internal/service/oidc"
|
||||||
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
||||||
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
||||||
|
@ -78,18 +79,19 @@ func exec(ctx context.Context, cfg *config.Config) error {
|
||||||
tokenService := tokenservice.NewTokenService(storage)
|
tokenService := tokenservice.NewTokenService(storage)
|
||||||
shortLogService := shortlogservice.NewShortLogService(storage)
|
shortLogService := shortlogservice.NewShortLogService(storage)
|
||||||
oidcService := oidcservice.NewOIDCService(ctx, cfg)
|
oidcService := oidcservice.NewOIDCService(ctx, cfg)
|
||||||
|
configService := configservice.NewConfigService(cfg)
|
||||||
|
|
||||||
// Start short log worker
|
// Start short log worker
|
||||||
stopWorker, _ := shortLogService.StartWorker(ctx)
|
stopWorker, _ := shortLogService.StartWorker(ctx)
|
||||||
defer stopWorker()
|
defer stopWorker()
|
||||||
|
|
||||||
// Create handlers
|
// Create handlers
|
||||||
apiHandler := apiserver.NewAPIHandler(shortService, userService, tokenService, shortLogService)
|
apiHandler := apiserver.NewAPIHandler(shortService, userService, tokenService, shortLogService, configService)
|
||||||
shortHandler := shortserver.NewShortHandler(shortService, shortLogService)
|
shortHandler := shortserver.NewShortHandler(shortService, shortLogService)
|
||||||
staticHandler := staticssterver.NewStaticHandler(cfg, "/", frontend.Assets())
|
staticHandler := staticssterver.NewStaticHandler(cfg, "/", frontend.Assets())
|
||||||
healthcheckHandler := healthcheckserver.NewHealthcheckHandler(storage)
|
healthcheckHandler := healthcheckserver.NewHealthcheckHandler(storage)
|
||||||
docsHandler := staticssterver.NewStaticHandler(cfg, "/api/docs", docs.Assets())
|
docsHandler := staticssterver.NewStaticHandler(cfg, "/api/docs", docs.Assets())
|
||||||
oidcHandler := oidcserver.NewOIDCHandler(oidcService, userService)
|
oidcHandler := oidcserver.NewOIDCHandler(cfg, oidcService, userService)
|
||||||
|
|
||||||
// Create routers
|
// Create routers
|
||||||
apiRouter := apiserver.NewAPIRouter(apiHandler)
|
apiRouter := apiserver.NewAPIRouter(apiHandler)
|
||||||
|
|
|
@ -71,6 +71,7 @@ func RegisterServerFlags(fs *flag.FlagSet, cfg *config.Config) {
|
||||||
fs.StringVar(&cfg.OIDCClientID, "oidc-client-id", "", "OIDC client ID")
|
fs.StringVar(&cfg.OIDCClientID, "oidc-client-id", "", "OIDC client ID")
|
||||||
fs.StringVar(&cfg.OIDCClientSecret, "oidc-client-secret", "", "OIDC client secret")
|
fs.StringVar(&cfg.OIDCClientSecret, "oidc-client-secret", "", "OIDC client secret")
|
||||||
fs.StringVar(&cfg.OIDCRedirectURL, "oidc-redirect-url", "", "OIDC redirect URL")
|
fs.StringVar(&cfg.OIDCRedirectURL, "oidc-redirect-url", "", "OIDC redirect URL")
|
||||||
|
fs.StringVar(&cfg.OIDCIssuerName, "oidc-issuer-name", config.DefaultOIDCIssuerName, "OIDC issuer name")
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitStorage initializes the storage depending on the config.
|
// InitStorage initializes the storage depending on the config.
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {
|
||||||
useSearchParams,
|
useSearchParams,
|
||||||
} from "react-router-dom"
|
} from "react-router-dom"
|
||||||
|
|
||||||
|
import { useServerConfig } from "../hooks/useServerConfig"
|
||||||
|
|
||||||
import Button from "./Button"
|
import Button from "./Button"
|
||||||
|
|
||||||
const actionOptions = {
|
const actionOptions = {
|
||||||
|
@ -102,6 +104,7 @@ const UserForm: FunctionComponent<{
|
||||||
}> = ({ action }) => {
|
}> = ({ action }) => {
|
||||||
const [params] = useSearchParams()
|
const [params] = useSearchParams()
|
||||||
const from = params.get("from") || "/"
|
const from = params.get("from") || "/"
|
||||||
|
const serverConfig = useServerConfig()
|
||||||
|
|
||||||
const opts = actionOptions[action]
|
const opts = actionOptions[action]
|
||||||
|
|
||||||
|
@ -128,11 +131,10 @@ const UserForm: FunctionComponent<{
|
||||||
const passwordScore = useMemo(() => passwordOMeter(password), [password])
|
const passwordScore = useMemo(() => passwordOMeter(password), [password])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<div className="flex flex-col mx-auto rounded-lg shadow-md p-8 max-w-md gap-y-6">
|
||||||
method="post"
|
|
||||||
replace
|
|
||||||
className="flex flex-col mx-auto gap-y-4 rounded-lg shadow-md p-8 max-w-min">
|
|
||||||
<span className="text-3xl font-bold text-center mb-4">{opts.title}</span>
|
<span className="text-3xl font-bold text-center mb-4">{opts.title}</span>
|
||||||
|
{serverConfig.disableCredentialLogin ? null : (
|
||||||
|
<Form method="post" replace className="flex flex-col gap-y-2">
|
||||||
{actionData && actionData.error ? (
|
{actionData && actionData.error ? (
|
||||||
<p className="text-red-500 text-center font-medium">
|
<p className="text-red-500 text-center font-medium">
|
||||||
{actionData.error}
|
{actionData.error}
|
||||||
|
@ -202,6 +204,16 @@ const UserForm: FunctionComponent<{
|
||||||
className="mt-6 px-8 py-3 max-w-fit mx-auto">
|
className="mt-6 px-8 py-3 max-w-fit mx-auto">
|
||||||
{isLoading ? opts.buttonLoadingText : opts.buttonText}
|
{isLoading ? opts.buttonLoadingText : opts.buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
{serverConfig?.oidcIssuerUrl.length && (
|
||||||
|
<a href={`${import.meta.env.VITE_OIDC_URL}/redirect`}>
|
||||||
|
<Button color="green" className="px-8 py-3 max-w-fit mx-auto">
|
||||||
|
{`${opts.buttonText} with ${serverConfig.oidcIssuerName}`}
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{serverConfig.disableRegistration && action === "login" ? null : (
|
||||||
<span className="text-slate-500 font-light text-center text-sm">
|
<span className="text-slate-500 font-light text-center text-sm">
|
||||||
{opts.altLabel + " "}
|
{opts.altLabel + " "}
|
||||||
<Link
|
<Link
|
||||||
|
@ -210,7 +222,8 @@ const UserForm: FunctionComponent<{
|
||||||
{opts.altLinkText}
|
{opts.altLinkText}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
</Form>
|
)}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useContext,
|
||||||
|
FC,
|
||||||
|
ReactNode,
|
||||||
|
} from "react"
|
||||||
|
|
||||||
|
import fetchAPI from "../util/fetchAPI"
|
||||||
|
|
||||||
|
// These configs mirror their server-side counterparts
|
||||||
|
|
||||||
|
export type ServerConfig = {
|
||||||
|
disableRegistration: boolean
|
||||||
|
disableCredentialLogin: boolean
|
||||||
|
oidcIssuerUrl: string
|
||||||
|
oidcIssuerName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultConfig: ServerConfig = {
|
||||||
|
disableRegistration: false,
|
||||||
|
disableCredentialLogin: false,
|
||||||
|
oidcIssuerUrl: "",
|
||||||
|
oidcIssuerName: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverContext = createContext<ServerConfig>(defaultConfig)
|
||||||
|
|
||||||
|
export const useServerConfig = () => {
|
||||||
|
return useContext(serverContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerConfigProvider: FC<{
|
||||||
|
children?: ReactNode
|
||||||
|
}> = ({ children }) => {
|
||||||
|
const [config, setConfig] = useState<ServerConfig>(defaultConfig)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchAPI<ServerConfig>("/config").then((result) => {
|
||||||
|
if (result.ok) {
|
||||||
|
setConfig(result.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<serverContext.Provider value={config}>{children}</serverContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import { StrictMode } from "react"
|
||||||
import { createRoot } from "react-dom/client"
|
import { createRoot } from "react-dom/client"
|
||||||
import { RouterProvider } from "react-router-dom"
|
import { RouterProvider } from "react-router-dom"
|
||||||
|
|
||||||
|
import { ServerConfigProvider } from "./hooks/useServerConfig.tsx"
|
||||||
import "./index.css"
|
import "./index.css"
|
||||||
import router from "./router.tsx"
|
import router from "./router.tsx"
|
||||||
|
|
||||||
|
@ -12,6 +13,8 @@ if (!rootEl) throw new Error("Root element not found")
|
||||||
|
|
||||||
createRoot(rootEl).render(
|
createRoot(rootEl).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
<ServerConfigProvider>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
|
</ServerConfigProvider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
)
|
)
|
||||||
|
|
|
@ -56,6 +56,8 @@ const (
|
||||||
DefaultQuiet = false
|
DefaultQuiet = false
|
||||||
// DefaultDisableCredentialsLogin is the default value for diable credential login.
|
// DefaultDisableCredentialsLogin is the default value for diable credential login.
|
||||||
DefaultDisableCredentialsLogin = false
|
DefaultDisableCredentialsLogin = false
|
||||||
|
// DefaultOIDCIssuerName is the default name of the OIDC issuer.
|
||||||
|
DefaultOIDCIssuerName = "OpenID"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -103,7 +105,9 @@ type Config struct {
|
||||||
// DisableCredentialsLogin defines whether or not the server should disable credential login.
|
// DisableCredentialsLogin defines whether or not the server should disable credential login.
|
||||||
DisableCredentialsLogin bool `json:"disableCredentialLogin"`
|
DisableCredentialsLogin bool `json:"disableCredentialLogin"`
|
||||||
// OIDCIssuerURL is the URL of the OIDC issuer.
|
// OIDCIssuerURL is the URL of the OIDC issuer.
|
||||||
OIDCIssuerURL string
|
OIDCIssuerURL string `json:"oidcIssuerUrl"`
|
||||||
|
// OIDCIssueName is the name of the OIDC issuer.
|
||||||
|
OIDCIssuerName string
|
||||||
// OIDCClientID is the client ID for OIDC.
|
// OIDCClientID is the client ID for OIDC.
|
||||||
OIDCClientID string
|
OIDCClientID string
|
||||||
// OIDCClientSecret is the client secret for OIDC.
|
// OIDCClientSecret is the client secret for OIDC.
|
||||||
|
@ -126,6 +130,7 @@ func NewConfig() *Config {
|
||||||
Verbose: DefaultVerbose,
|
Verbose: DefaultVerbose,
|
||||||
Quiet: DefaultQuiet,
|
Quiet: DefaultQuiet,
|
||||||
DisableCredentialsLogin: DefaultDisableCredentialsLogin,
|
DisableCredentialsLogin: DefaultDisableCredentialsLogin,
|
||||||
|
OIDCIssuerName: DefaultOIDCIssuerName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"git.maronato.dev/maronato/goshort/internal/errs"
|
"git.maronato.dev/maronato/goshort/internal/errs"
|
||||||
"git.maronato.dev/maronato/goshort/internal/server"
|
"git.maronato.dev/maronato/goshort/internal/server"
|
||||||
authmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/auth"
|
authmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/auth"
|
||||||
|
configservice "git.maronato.dev/maronato/goshort/internal/service/config"
|
||||||
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
||||||
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
||||||
tokenservice "git.maronato.dev/maronato/goshort/internal/service/token"
|
tokenservice "git.maronato.dev/maronato/goshort/internal/service/token"
|
||||||
|
@ -27,6 +28,7 @@ type APIHandler struct {
|
||||||
users *userservice.UserService
|
users *userservice.UserService
|
||||||
tokens *tokenservice.TokenService
|
tokens *tokenservice.TokenService
|
||||||
shortLogs *shortlogservice.ShortLogService
|
shortLogs *shortlogservice.ShortLogService
|
||||||
|
config *configservice.ConfigService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPIHandler(
|
func NewAPIHandler(
|
||||||
|
@ -34,12 +36,14 @@ func NewAPIHandler(
|
||||||
users *userservice.UserService,
|
users *userservice.UserService,
|
||||||
tokens *tokenservice.TokenService,
|
tokens *tokenservice.TokenService,
|
||||||
shortLogs *shortlogservice.ShortLogService,
|
shortLogs *shortlogservice.ShortLogService,
|
||||||
|
config *configservice.ConfigService,
|
||||||
) *APIHandler {
|
) *APIHandler {
|
||||||
return &APIHandler{
|
return &APIHandler{
|
||||||
shorts: shorts,
|
shorts: shorts,
|
||||||
users: users,
|
users: users,
|
||||||
tokens: tokens,
|
tokens: tokens,
|
||||||
shortLogs: shortLogs,
|
shortLogs: shortLogs,
|
||||||
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +175,14 @@ func (h *APIHandler) Signup(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
l := logging.FromCtx(ctx)
|
l := logging.FromCtx(ctx)
|
||||||
|
|
||||||
|
if h.config.GetPublicConfig().DisableCredentialsLogin {
|
||||||
|
l.Debug("credentials registration is disabled")
|
||||||
|
|
||||||
|
server.RenderForbidden(w, r)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get the user from the json body
|
// Get the user from the json body
|
||||||
type signupForm struct {
|
type signupForm struct {
|
||||||
models.User
|
models.User
|
||||||
|
@ -578,6 +590,21 @@ func (h *APIHandler) DeleteToken(w http.ResponseWriter, r *http.Request) {
|
||||||
render.NoContent(w, r)
|
render.NoContent(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicConfig returns the public configuration of the server.
|
||||||
|
func (h *APIHandler) PublicConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, span := tracing.StartSpan(r.Context(), "api.PublicConfig")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
// Get public config
|
||||||
|
config := h.config.GetPublicConfig()
|
||||||
|
|
||||||
|
span.AddEvent("got public config")
|
||||||
|
|
||||||
|
// Render the response
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
render.JSON(w, r, config)
|
||||||
|
}
|
||||||
|
|
||||||
// findUserOrRespond is a helper function that finds a user in the session,
|
// findUserOrRespond is a helper function that finds a user in the session,
|
||||||
// and returns it. If the user is not found, it returns nil and false.
|
// and returns it. If the user is not found, it returns nil and false.
|
||||||
func (h *APIHandler) findUserOrRespond(w http.ResponseWriter, r *http.Request) (user *models.User, ok bool) {
|
func (h *APIHandler) findUserOrRespond(w http.ResponseWriter, r *http.Request) (user *models.User, ok bool) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
apiserver "git.maronato.dev/maronato/goshort/internal/server/api"
|
apiserver "git.maronato.dev/maronato/goshort/internal/server/api"
|
||||||
servermiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware"
|
servermiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware"
|
||||||
authmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/auth"
|
authmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/auth"
|
||||||
|
configservice "git.maronato.dev/maronato/goshort/internal/service/config"
|
||||||
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
|
||||||
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
|
||||||
tokenservice "git.maronato.dev/maronato/goshort/internal/service/token"
|
tokenservice "git.maronato.dev/maronato/goshort/internal/service/token"
|
||||||
|
@ -62,8 +63,9 @@ func setup(ctx context.Context, cfg *config.Config) (
|
||||||
users := userservice.NewUserService(cfg, str)
|
users := userservice.NewUserService(cfg, str)
|
||||||
tokens := tokenservice.NewTokenService(str)
|
tokens := tokenservice.NewTokenService(str)
|
||||||
shortLogs := shortlogservice.NewShortLogService(str)
|
shortLogs := shortlogservice.NewShortLogService(str)
|
||||||
|
cfgService := configservice.NewConfigService(cfg)
|
||||||
|
|
||||||
api := apiserver.NewAPIHandler(shorts, users, tokens, shortLogs)
|
api := apiserver.NewAPIHandler(shorts, users, tokens, shortLogs, cfgService)
|
||||||
|
|
||||||
return api, shorts, users, tokens, shortLogs
|
return api, shorts, users, tokens, shortLogs
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ func NewAPIRouter(handler *APIHandler) http.Handler {
|
||||||
mux.Post("/logout", handler.Logout)
|
mux.Post("/logout", handler.Logout)
|
||||||
mux.Post("/signup", handler.Signup)
|
mux.Post("/signup", handler.Signup)
|
||||||
|
|
||||||
|
// Public routes
|
||||||
|
mux.Get("/config", handler.PublicConfig)
|
||||||
|
|
||||||
// Authenticated routes
|
// Authenticated routes
|
||||||
mux.Group(func(r chi.Router) {
|
mux.Group(func(r chi.Router) {
|
||||||
// UI and API endpoints
|
// UI and API endpoints
|
||||||
|
|
|
@ -18,6 +18,8 @@ import (
|
||||||
type Server struct {
|
type Server struct {
|
||||||
// apiUrl is API server's URL
|
// apiUrl is API server's URL
|
||||||
apiURL string
|
apiURL string
|
||||||
|
// oidcURL is OIDC server's URL
|
||||||
|
oidcURL string
|
||||||
// uiPort is the port the UI server will listen on
|
// uiPort is the port the UI server will listen on
|
||||||
uiPort string
|
uiPort string
|
||||||
// host is the host the UI server will listen on
|
// host is the host the UI server will listen on
|
||||||
|
@ -33,9 +35,15 @@ func NewServer(cfg *config.Config) *Server {
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Path: "/api",
|
Path: "/api",
|
||||||
}
|
}
|
||||||
|
oidcURL := url.URL{
|
||||||
|
Host: net.JoinHostPort(cfg.Host, cfg.Port),
|
||||||
|
Scheme: "http",
|
||||||
|
Path: "/oidc",
|
||||||
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
apiURL: apiURL.String(),
|
apiURL: apiURL.String(),
|
||||||
|
oidcURL: oidcURL.String(),
|
||||||
uiPort: cfg.UIPort,
|
uiPort: cfg.UIPort,
|
||||||
host: cfg.Host,
|
host: cfg.Host,
|
||||||
cancel: func() {},
|
cancel: func() {},
|
||||||
|
@ -86,7 +94,7 @@ func (s *Server) Start(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the API_URL env var
|
// Set the API_URL env var
|
||||||
cmd.Env = append(os.Environ(), "VITE_API_URL="+s.apiURL)
|
cmd.Env = append(os.Environ(), "VITE_API_URL="+s.apiURL, "VITE_OIDC_URL="+s.oidcURL)
|
||||||
|
|
||||||
// Use the current process's stdout
|
// Use the current process's stdout
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
|
|
|
@ -2,8 +2,11 @@ package oidcserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"git.maronato.dev/maronato/goshort/internal/config"
|
||||||
"git.maronato.dev/maronato/goshort/internal/errs"
|
"git.maronato.dev/maronato/goshort/internal/errs"
|
||||||
"git.maronato.dev/maronato/goshort/internal/server"
|
"git.maronato.dev/maronato/goshort/internal/server"
|
||||||
authmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/auth"
|
authmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/auth"
|
||||||
|
@ -19,12 +22,16 @@ import (
|
||||||
type OIDCHandler struct {
|
type OIDCHandler struct {
|
||||||
oidc *oidcservice.OIDCService
|
oidc *oidcservice.OIDCService
|
||||||
user *userservice.UserService
|
user *userservice.UserService
|
||||||
|
uiHost string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOIDCHandler(oidc *oidcservice.OIDCService, user *userservice.UserService) *OIDCHandler {
|
func NewOIDCHandler(cfg *config.Config, oidc *oidcservice.OIDCService, user *userservice.UserService) *OIDCHandler {
|
||||||
|
uiHost := net.JoinHostPort(cfg.Host, cfg.UIPort)
|
||||||
|
|
||||||
return &OIDCHandler{
|
return &OIDCHandler{
|
||||||
oidc: oidc,
|
oidc: oidc,
|
||||||
user: user,
|
user: user,
|
||||||
|
uiHost: uiHost,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +87,6 @@ func (h *OIDCHandler) Callback(w http.ResponseWriter, r *http.Request) {
|
||||||
user.SetNoLoginPassword()
|
user.SetNoLoginPassword()
|
||||||
|
|
||||||
user, err = h.user.CreateUser(ctx, user)
|
user, err = h.user.CreateUser(ctx, user)
|
||||||
|
|
||||||
// Handle errors
|
// Handle errors
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
|
@ -115,5 +121,10 @@ func (h *OIDCHandler) Callback(w http.ResponseWriter, r *http.Request) {
|
||||||
span.AddEvent("logged in user")
|
span.AddEvent("logged in user")
|
||||||
|
|
||||||
// Redirect to the home page
|
// Redirect to the home page
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
uiURL := url.URL{
|
||||||
|
Scheme: r.URL.Scheme,
|
||||||
|
Host: h.uiHost,
|
||||||
|
Path: "/",
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, uiURL.String(), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package configservice
|
||||||
|
|
||||||
|
import "git.maronato.dev/maronato/goshort/internal/config"
|
||||||
|
|
||||||
|
type PublicConfig struct {
|
||||||
|
// DisableRegistration defines whether or not registration are disabled.
|
||||||
|
DisableRegistration bool `json:"disableRegistration"`
|
||||||
|
// DisableCredentialsLogin defines whether or not the server should disable credential login.
|
||||||
|
DisableCredentialsLogin bool `json:"disableCredentialLogin"`
|
||||||
|
// OIDCIssuerURL is the URL of the OIDC issuer.
|
||||||
|
OIDCIssuerURL string `json:"oidcIssuerUrl"`
|
||||||
|
// OIDCIssuerName is the name of the OIDC issuer.
|
||||||
|
OIDCIssuerName string `json:"oidcIssuerName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigService is the service that handles the configuration.
|
||||||
|
type ConfigService struct {
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfigService creates a new configuration service.
|
||||||
|
func NewConfigService(cfg *config.Config) *ConfigService {
|
||||||
|
return &ConfigService{
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicConfigJSON returns the public configuration as a JSON string.
|
||||||
|
func (s *ConfigService) GetPublicConfig() *PublicConfig {
|
||||||
|
return &PublicConfig{
|
||||||
|
DisableRegistration: s.cfg.DisableRegistration,
|
||||||
|
DisableCredentialsLogin: s.cfg.DisableCredentialsLogin,
|
||||||
|
OIDCIssuerURL: s.cfg.OIDCIssuerURL,
|
||||||
|
OIDCIssuerName: s.cfg.OIDCIssuerName,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user