Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Registry UI #625

Merged
merged 12 commits into from
Jan 10, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist/
README.dev.md
docs/parameters.json
local
1 change: 1 addition & 0 deletions images/dev-config.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
OriginUrl: https://localhost:8444
TLSSkipVerify: true

10 changes: 8 additions & 2 deletions registry/registry_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,12 @@ func updateNamespace(ns *Namespace) error {
if err != nil || existingNs == nil {
return errors.Wrap(err, "Failed to get namespace")
}
if ns.Prefix == "" {
ns.Prefix = existingNs.Prefix
}
if ns.Pubkey == "" {
ns.Pubkey = existingNs.Pubkey
}
existingNsAdmin := existingNs.AdminMetadata
// We prevent the following fields from being modified by the user for now.
// They are meant for "internal" use only and we don't support changing
Expand All @@ -602,12 +608,12 @@ func updateNamespace(ns *Namespace) error {

// We intentionally exclude updating "identity" as this should only be updated
// when user registered through Pelican client with identity
query := `UPDATE namespace SET pubkey = ?, admin_metadata = ? WHERE id = ?`
query := `UPDATE namespace SET prefix = ?, pubkey = ?, admin_metadata = ? WHERE id = ?`
tx, err := db.Begin()
if err != nil {
return err
}
_, err = tx.Exec(query, ns.Pubkey, strAdminMetadata, ns.ID)
_, err = tx.Exec(query, ns.Prefix, ns.Pubkey, strAdminMetadata, ns.ID)
if err != nil {
if errRoll := tx.Rollback(); errRoll != nil {
log.Errorln("Failed to rollback transaction:", errRoll)
Expand Down
10 changes: 6 additions & 4 deletions registry/registry_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ func createUpdateNamespace(ctx *gin.Context, isUpdate bool) {
ctx.JSON(400, gin.H{"error": "Invalid create or update namespace request"})
return
}
// Assign ID from path param because the request data doesn't have ID set
ns.ID = id
// Basic validation (type, required, etc)
errs := config.GetValidate().Struct(ns)
if errs != nil {
Expand Down Expand Up @@ -376,25 +378,25 @@ func createUpdateNamespace(ctx *gin.Context, isUpdate bool) {
// Then check if the user has previlege to update
isAdmin, _ := web_ui.CheckAdmin(user)
if !isAdmin { // Not admin, need to check if the namespace belongs to the user
found, err := namespaceBelongsToUserId(id, user)
found, err := namespaceBelongsToUserId(ns.ID, user)
if err != nil {
log.Error("Error checking if namespace belongs to the user: ", err)
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Error checking if namespace belongs to the user"})
return
}
if !found {
log.Errorf("Namespace not found for id: %d", id)
log.Errorf("Namespace not found for id: %d", ns.ID)
ctx.JSON(http.StatusNotFound, gin.H{"error": "Namespace not found. Check the id or if you own the namespace"})
return
}
existingStatus, err := getNamespaceStatusById(id)
existingStatus, err := getNamespaceStatusById(ns.ID)
if err != nil {
log.Error("Error checking namespace status: ", err)
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Error checking namespace status"})
return
}
if existingStatus == Approved {
log.Errorf("User '%s' is trying to modify approved namespace registration with id=%d", user, id)
log.Errorf("User '%s' is trying to modify approved namespace registration with id=%d", user, ns.ID)
ctx.JSON(http.StatusForbidden, gin.H{"error": "You don't have permission to modify an approved registration. Please contact your federation administrator"})
return
}
Expand Down
2 changes: 1 addition & 1 deletion web_ui/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ as they would in production.
# From repo root
make web-build
goreleaser --clean --snapshot
docker run --rm -it -p 8444:8444 -w /app -v $PWD/dist/pelican_linux_arm64/:/app hub.opensciencegrid.org/pelican_platform/pelican-dev:latest-itb /bin/bash
docker run --rm -it -p 8444:8444 -w /app -v $PWD/dist/pelican_linux_arm64/:/app -v $PWD/local/:/etc/pelican/ pelican-dev /bin/bash
```

```shell
Expand Down
1 change: 1 addition & 0 deletions web_ui/frontend/app/(login)/components/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default function PasswordInput({FormControlProps, TextFieldProps}: Passwo
<TextField
label="Password"
id="outlined-start-adornment"
size={"small"}
sx={{ m: 1, width: '50ch' }}
type={showPassword ? 'text' : 'password'}
{...TextFieldProps}
Expand Down
58 changes: 47 additions & 11 deletions web_ui/frontend/app/(login)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

"use client"

import {Box, Grow, Typography} from "@mui/material";
import {Box, Grow, Typography, Button} from "@mui/material";
import { useRouter } from 'next/navigation'
import { useState } from "react";
import {useEffect, useState} from "react";

import LoadingButton from "../components/LoadingButton";

Expand All @@ -31,8 +31,19 @@ export default function Home() {
const router = useRouter()
let [password, setPassword] = useState <string>("")
let [loading, setLoading] = useState(false);
let [enabledServers, setEnabledServers] = useState<string[]>([])
let [error, setError] = useState<string | undefined>(undefined);

useEffect(() => {
(async () => {
const response = await fetch("/api/v1.0/servers")
if (response.ok) {
const data = await response.json()
setEnabledServers(data)
}
})()
}, []);

async function submit(password: string) {

setLoading(true)
Expand Down Expand Up @@ -88,18 +99,43 @@ export default function Home() {
</Typography>
</Box>
<Box pt={2} mx={"auto"}>
{ enabledServers !== undefined && enabledServers.includes("registry") &&
<>
<Typography textAlign={"center"} variant={"h6"} component={"h2"}>
For Outside Administrators
</Typography>
<Box display={"flex"} justifyContent={"center"} my={2} mb={3}>
<Button
size={"large"}
href={"/api/v1.0/auth/cilogon/login?next_url=/view/registry/"}
variant={"contained"}
>
Login with CILogon
</Button>
</Box>
<Typography sx={{color: "#646464"}} textAlign={"center"} variant={"subtitle1"} component={"h2"}>
For Registry Administrator
</Typography>
</>
}
<form onSubmit={onSubmit} action="#">
<Box>
<PasswordInput TextFieldProps={{
InputProps: {
onChange: (e) => {
setPassword(e.target.value)
setError(undefined)
<Box display={"flex"} justifyContent={"center"}>
<PasswordInput
FormControlProps={{
sx: {width: "50%"},
}}
TextFieldProps={{
InputProps: {
sx: {width: "50%"},
onChange: (e) => {
setPassword(e.target.value)
setError(undefined)
}
}
}
}}/>
}}
/>
</Box>
<Box mt={3} display={"flex"} flexDirection={"column"}>
<Box display={"flex"} flexDirection={"column"}>
<Grow in={error !== undefined}>
<Typography
textAlign={"center"}
Expand Down
15 changes: 12 additions & 3 deletions web_ui/frontend/app/registry/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import Link from "next/link";
import Image from "next/image";
import PelicanLogo from "@/public/static/images/PelicanPlatformLogo_Icon.png";
import IconButton from "@mui/material/IconButton";
import BuildIcon from "@mui/icons-material/Build";
import {Add, Build} from "@mui/icons-material";

export const metadata = {
title: 'Pelican Registry',
Expand All @@ -38,19 +38,28 @@ export default function RootLayout({
return (
<Box display={"flex"} flexDirection={"row"}>
<Sidebar>
<Link href={"/index.html"}>
<Link href={"/registry/index.html"}>
<Image
src={PelicanLogo}
alt={"Pelican Logo"}
width={36}
height={36}
/>
</Link>
<Box pt={1}>
<Tooltip title={"Register Namespace"} placement={"right"}>
<Link href={"/registry/namespace/register/index.html"}>
<IconButton>
<Add/>
</IconButton>
</Link>
</Tooltip>
</Box>
<Box pt={1}>
<Tooltip title={"Config"} placement={"right"}>
<Link href={"/config/index.html"}>
<IconButton>
<BuildIcon/>
<Build/>
</IconButton>
</Link>
</Tooltip>
Expand Down
Loading