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

Registry UI APIs #515

Merged
merged 17 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions cmd/registry_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,12 @@ func serveRegistry( /*cmd*/ *cobra.Command /*args*/, []string) error {
}

rootRouterGroup := engine.Group("/")
// Call out to registry to establish routes for the gin engine
registry.RegisterRegistryRoutes(rootRouterGroup)
registry.RegisterRegistryWebAPI(rootRouterGroup)
// Register routes for server/Pelican client facing APIs
registry.RegisterRegistryAPI(rootRouterGroup)
// Register routes for APIs to registry Web UI
if err := registry.RegisterRegistryWebAPI(rootRouterGroup); err != nil {
return err
}
log.Info("Starting web engine...")

// Might need to play around with this setting more to handle
Expand Down
25 changes: 20 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"syscall"
"time"

"github.com/go-playground/validator/v10"
"github.com/pelicanplatform/pelican/param"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -117,11 +118,18 @@ var (
transport *http.Transport
onceTransport sync.Once

// Global struct validator
validate *validator.Validate

// A variable indicating enabled Pelican servers in the current process
enabledServers ServerType
setServerOnce sync.Once
)

func init() {
validate = validator.New(validator.WithRequiredStructEnabled())
}

// Set sets a list of newServers to ServerType instance
func (sType *ServerType) Set(newServers []ServerType) {
for _, server := range newServers {
Expand Down Expand Up @@ -416,6 +424,11 @@ func GetTransport() *http.Transport {
return transport
}

// Get singleton global validte method for field validation
func GetValidate() *validator.Validate {
return validate
}

func InitConfig() {
viper.SetConfigType("yaml")
// 1) Set up defaults.yaml
Expand Down Expand Up @@ -534,6 +547,7 @@ func InitServer(enabledServers []ServerType, currentServer ServerType) error {
viper.SetDefault("IssuerKey", filepath.Join(configDir, "issuer.jwk"))
viper.SetDefault("Server.UIPasswordFile", filepath.Join(configDir, "server-web-passwd"))
viper.SetDefault("Server.UIActivationCodeFile", filepath.Join(configDir, "server-web-activation-code"))
viper.SetDefault("Server.SessionSecretFile", filepath.Join(configDir, "session-secret"))
viper.SetDefault("OIDC.ClientIDFile", filepath.Join(configDir, "oidc-client-id"))
viper.SetDefault("OIDC.ClientSecretFile", filepath.Join(configDir, "oidc-client-secret"))
viper.SetDefault("Cache.ExportLocation", "/")
Expand Down Expand Up @@ -607,8 +621,6 @@ func InitServer(enabledServers []ServerType, currentServer ServerType) error {
return errors.Wrap(err, fmt.Sprint("Invalid Server.ExternalWebUrl: ", externalAddressStr))
}

setupTransport()

tokenRefreshInterval := param.Monitoring_TokenRefreshInterval.GetDuration()
tokenExpiresIn := param.Monitoring_TokenExpiresIn.GetDuration()

Expand Down Expand Up @@ -639,15 +651,18 @@ func InitServer(enabledServers []ServerType, currentServer ServerType) error {
return err
}

// Generate the session cookie secret and save it as the default value
err = GenerateSessionSecret()
if err != nil {
// Generate the session secret and save it as the default value
if err := GenerateSessionSecret(); err != nil {
return err
}

// After we know we have the certs we need, call setupTransport (which uses those certs for its TLSConfig)
setupTransport()

// Setup CSRF middleware. To use it, you need to add this middleware to your chain
// of http handlers by calling config.GetCSRFHandler()
setupCSRFHandler()

// Set up the server's issuer URL so we can access that data wherever we need to find keys and whatnot
// This populates Server.IssuerUrl, and can be safely fetched using server_utils.GetServerIssuerURL()
err = parseServerIssuerURL(currentServer)
Expand Down
66 changes: 66 additions & 0 deletions config/csrf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/***************************************************************
*
* Copyright (C) 2023, Pelican Project, Morgridge Institute for Research
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************/

package config

import (
"net/http"
"sync"

"github.com/gin-gonic/gin"
"github.com/gorilla/csrf"
adapter "github.com/gwatts/gin-adapter"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)

var (
// Global CSRF handler that shares the same auth key
csrfHanlder gin.HandlerFunc
onceCSRFHanlder sync.Once
)

func setupCSRFHandler() {
csrfKey, err := LoadSessionSecret()
if err != nil {
log.Error("Error loading session secret, abort setting up CSRF handler:", err)
return
}
CSRF := csrf.Protect(csrfKey,
csrf.SameSite(csrf.SameSiteStrictMode),
csrf.Path("/"),
csrf.ErrorHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden)
_, err := w.Write([]byte(`{"message": "CSRF token invalid"}`))
if err != nil {
log.Error("Error writing error message back as response")
}
})),
)
csrfHanlder = adapter.Wrap(CSRF)
}

func GetCSRFHandler() (gin.HandlerFunc, error) {
onceCSRFHanlder.Do(func() {
setupCSRFHandler()
})
if csrfHanlder == nil {
return nil, errors.New("Error setting up the CSRF hanlder")
}
return csrfHanlder, nil
}
7 changes: 7 additions & 0 deletions config/init_server_creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,12 +624,19 @@ func GenerateSessionSecret() error {
return nil
}

// Load session secret from Server_SessionSecretFile. Generate session secret
// if no file present.
func LoadSessionSecret() ([]byte, error) {
secretLocation := param.Server_SessionSecretFile.GetString()

if secretLocation == "" {
return []byte{}, errors.New("Empty filename for Server_SessionSecretFile")
}

if err := GenerateSessionSecret(); err != nil {
return []byte{}, err
}

rest, err := os.ReadFile(secretLocation)
if err != nil {
return []byte{}, errors.Wrap(err, "Error reading secret file")
Expand Down
32 changes: 30 additions & 2 deletions docs/parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,33 @@ type: bool
default: true
components: ["nsregistry"]
---
name: Registry.AdminUsers
description: >-
A string slice of "subject" claim of users to give admin permission for registry UI.

The "subject" claim should be the "CILogon User Identifier" from CILogon user page: https://cilogon.org/
type: stringSlice
default: []
components: ["nsregistry"]
---
name: Registry.Institutions
description: >-
A array of institution objects available to register. Users can only select from this list
when they register a new namespace. Each object has `name` and `id` field where
`name` is a human-readable name for the institution and `id` is a unique identifier
for the institution. For Pelican running in OSDF alias, the `id` will be OSG ID.

For example:

```
- name: University of Wisconsin - Madison
id: https://osg-htc.org/iid/01y2jtd41
```

type: object
default: none
components: ["nsregistry"]
---
############################
# Server-level configs #
############################
Expand Down Expand Up @@ -648,7 +675,7 @@ components: ["origin", "director", "nsregistry"]
name: Server.IssuerJwks
description: >-
A filepath indicating where the server's public JSON web keyset can be found.
type: string
type: filename
default: none
components: ["origin", "director", "nsregistry"]
---
Expand All @@ -671,7 +698,8 @@ name: Server.SessionSecretFile
description: >-
The filepath to the secret for encrypt/decrypt session data for Pelican web UI to initiate a session cookie

This is only used for sending redirect request for OAuth2 authentication flow as of 11/22/2023
This is used for sending redirect request for OAuth2 authentication follow.
This is also used for CSRF auth key.
type: filename
default: $ConfigBase/session-secret
The default content of the file is the hash of the concatenation of "pelican" and the DER form of ${IssuerKey}
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ require (
github.com/go-ini/ini v1.67.0
github.com/go-kit/log v0.2.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/gorilla/csrf v1.7.2
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd
github.com/gwatts/gin-adapter v1.0.0
github.com/hashicorp/go-version v1.6.0
github.com/jellydator/ttlcache/v3 v3.1.0
github.com/jsipprell/keyctl v1.0.4-0.20211208153515-36ca02672b6c
Expand Down Expand Up @@ -48,7 +50,7 @@ require (

require (
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
)

Expand Down Expand Up @@ -91,7 +93,7 @@ require (
github.com/go-openapi/validate v0.22.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-playground/validator/v10 v10.16.0
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
Expand Down
15 changes: 12 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
Expand Down Expand Up @@ -320,13 +320,22 @@ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8
github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww=
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A=
github.com/gwatts/gin-adapter v1.0.0 h1:TsmmhYTR79/RMTsfYJ2IQvI1F5KZ3ZFJxuQSYEOpyIA=
github.com/gwatts/gin-adapter v1.0.0/go.mod h1:44AEV+938HsS0mjfXtBDCUZS9vONlF2gwvh8wu4sRYc=
github.com/hashicorp/consul/api v1.22.0 h1:ydEvDooB/A0c/xpsBd8GSt7P2/zYPBui4KrNip0xGjE=
github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
Expand Down
2 changes: 2 additions & 0 deletions param/parameters.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions param/parameters_struct.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions registry/client_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ func NamespaceRegister(privateKey jwk.Key, namespaceRegistryEndpoint string, acc
return errors.Wrapf(err, "Failed to make request: %v", respData.Error)
}
fmt.Println(respData.Message)
} else {
if err != nil {
return errors.Wrapf(err, "Failed to make request: %s", resp)
}
return errors.Wrapf(unmarshalErr, "Failed to unmarshall request response: %v", respData.Error)
}

return nil
Expand Down
5 changes: 2 additions & 3 deletions registry/client_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,13 @@ func registryMockup(t *testing.T, testName string) *httptest.Server {
err := config.InitServer([]config.ServerType{config.RegistryType}, config.RegistryType)
require.NoError(t, err)

err = InitializeDB()
require.NoError(t, err)
setupMockRegistryDB(t)

gin.SetMode(gin.TestMode)
engine := gin.Default()

//Configure registry
RegisterRegistryRoutes(engine.Group("/"))
RegisterRegistryAPI(engine.Group("/"))

//Set up a server to use for testing
svr := httptest.NewServer(engine)
Expand Down
Loading
Loading