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  }