github.com/pelicanplatform/pelican@v1.0.5/client/sharing_url_test.go (about) 1 /*************************************************************** 2 * 3 * Copyright (C) 2023, University of Nebraska-Lincoln 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); you 6 * may not use this file except in compliance with the License. You may 7 * obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ***************************************************************/ 18 19 package client 20 21 import ( 22 "fmt" 23 "io" 24 "net/http" 25 "net/http/httptest" 26 "net/url" 27 "os" 28 "strings" 29 "testing" 30 31 "github.com/pelicanplatform/pelican/config" 32 log "github.com/sirupsen/logrus" 33 "github.com/spf13/viper" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 ) 37 38 func TestDirectorGeneration(t *testing.T) { 39 returnError := false 40 returnErrorRef := &returnError 41 42 handler := func(w http.ResponseWriter, r *http.Request) { 43 discoveryConfig := `{"director_endpoint": "https://location.example.com", "namespace_registration_endpoint": "https://location.example.com/namespace", "jwks_uri": "https://location.example.com/jwks"}` 44 if *returnErrorRef { 45 w.WriteHeader(http.StatusInternalServerError) 46 } else { 47 w.WriteHeader(http.StatusOK) 48 _, err := w.Write([]byte(discoveryConfig)) 49 assert.NoError(t, err) 50 } 51 } 52 server := httptest.NewTLSServer(http.HandlerFunc(handler)) 53 defer server.Close() 54 serverURL, err := url.Parse(server.URL) 55 require.NoError(t, err) 56 57 objectUrl := url.URL{ 58 Scheme: "pelican", 59 Host: serverURL.Host, 60 Path: "/test/foo", 61 } 62 63 // Discovery works to get URL 64 viper.Reset() 65 viper.Set("TLSSkipVerify", true) 66 err = config.InitClient() 67 require.NoError(t, err) 68 dUrl, err := getDirectorFromUrl(&objectUrl) 69 require.NoError(t, err) 70 assert.Equal(t, dUrl, "https://location.example.com") 71 72 // Discovery URL overrides the federation config. 73 viper.Reset() 74 viper.Set("TLSSkipVerify", true) 75 viper.Set("Federation.DirectorURL", "https://location2.example.com") 76 dUrl, err = getDirectorFromUrl(&objectUrl) 77 require.NoError(t, err) 78 assert.Equal(t, dUrl, "https://location.example.com") 79 80 // Fallback to configuration if no discovery present 81 viper.Reset() 82 viper.Set("Federation.DirectorURL", "https://location2.example.com") 83 objectUrl.Host = "" 84 dUrl, err = getDirectorFromUrl(&objectUrl) 85 require.NoError(t, err) 86 assert.Equal(t, dUrl, "https://location2.example.com") 87 88 // Error if server has an error 89 viper.Reset() 90 returnError = true 91 viper.Set("TLSSkipVerify", true) 92 objectUrl.Host = serverURL.Host 93 _, err = getDirectorFromUrl(&objectUrl) 94 require.Error(t, err) 95 96 // Error if neither config nor hostname provided. 97 viper.Reset() 98 objectUrl.Host = "" 99 _, err = getDirectorFromUrl(&objectUrl) 100 require.Error(t, err) 101 102 // Error on unknown scheme 103 viper.Reset() 104 objectUrl.Scheme = "buzzard" 105 _, err = getDirectorFromUrl(&objectUrl) 106 require.Error(t, err) 107 } 108 109 func TestSharingUrl(t *testing.T) { 110 // Construct a local server that we can poke with QueryDirector 111 myUrl := "http://redirect.com" 112 myUrlRef := &myUrl 113 log.SetLevel(log.DebugLevel) 114 handler := func(w http.ResponseWriter, r *http.Request) { 115 issuerLoc := *myUrlRef + "/issuer" 116 117 if strings.HasPrefix(r.URL.Path, "/test") { 118 w.Header().Set("Location", *myUrlRef) 119 w.Header().Set("X-Pelican-Namespace", "namespace=/test, require-token=true") 120 w.Header().Set("X-Pelican-Authorization", fmt.Sprintf("issuer=%s", issuerLoc)) 121 w.Header().Set("X-Pelican-Token-Generation", fmt.Sprintf("issuer=%s, base-path=/test, strategy=OAuth2", issuerLoc)) 122 w.WriteHeader(http.StatusTemporaryRedirect) 123 } else if r.URL.Path == "/issuer/.well-known/openid-configuration" { 124 w.WriteHeader(http.StatusOK) 125 oidcConfig := fmt.Sprintf(`{"token_endpoint": "%s/token", "registration_endpoint": "%s/register", "grant_types_supported": ["urn:ietf:params:oauth:grant-type:device_code"], "device_authorization_endpoint": "%s/device_authz"}`, issuerLoc, issuerLoc, issuerLoc) 126 _, err := w.Write([]byte(oidcConfig)) 127 assert.NoError(t, err) 128 } else if r.URL.Path == "/issuer/register" { 129 //requestBytes, err := io.ReadAll(r.Body) 130 //assert.NoError(t, err) 131 clientConfig := `{"client_id": "client1", "client_secret": "secret", "client_secret_expires_at": 0}` 132 w.WriteHeader(http.StatusCreated) 133 _, err := w.Write([]byte(clientConfig)) 134 assert.NoError(t, err) 135 } else if r.URL.Path == "/issuer/device_authz" { 136 w.WriteHeader(http.StatusOK) 137 _, err := w.Write([]byte(`{"device_code": "1234", "user_code": "5678", "interval": 1, "verification_uri": "https://example.com", "expires_in": 20}`)) 138 assert.NoError(t, err) 139 } else if r.URL.Path == "/issuer/token" { 140 w.Header().Set("Content-Type", "application/json") 141 w.WriteHeader(http.StatusOK) 142 _, err := w.Write([]byte(`{"access_token": "token1234", "token_type": "jwt"}`)) 143 assert.NoError(t, err) 144 } else { 145 fmt.Println(r) 146 requestBytes, err := io.ReadAll(r.Body) 147 assert.NoError(t, err) 148 fmt.Println(string(requestBytes)) 149 w.WriteHeader(http.StatusInternalServerError) 150 } 151 } 152 server := httptest.NewServer(http.HandlerFunc(handler)) 153 defer server.Close() 154 myUrl = server.URL 155 156 os.Setenv("PELICAN_SKIP_TERMINAL_CHECK", "password") 157 defer os.Unsetenv("PELICAN_SKIP_TERMINAL_CHECK") 158 viper.Set("Federation.DirectorURL", myUrl) 159 viper.Set("ConfigDir", t.TempDir()) 160 err := config.InitClient() 161 assert.NoError(t, err) 162 163 // Call QueryDirector with the test server URL and a source path 164 testUrl, err := url.Parse("/test/foo/bar") 165 require.NoError(t, err) 166 token, err := CreateSharingUrl(testUrl, true) 167 assert.NoError(t, err) 168 assert.NotEmpty(t, token) 169 fmt.Println(token) 170 }