예제 #1
0
// CreateNamedTemporaryCredentials generates temporary credentials from permanent
// credentials, valid for the given duration, starting immediately.  The
// temporary credentials' scopes must be a subset of the permanent credentials'
// scopes. The duration may not be more than 31 days. Any authorized scopes of
// the permanent credentials will be passed through as authorized scopes to the
// temporary credentials, but will not be restricted via the certificate.
//
// See https://docs.taskcluster.net/manual/apis/temporary-credentials
func (permaCreds *Credentials) CreateNamedTemporaryCredentials(tempClientID string, duration time.Duration, scopes ...string) (tempCreds *Credentials, err error) {
	if duration > 31*24*time.Hour {
		return nil, errors.New("Temporary credentials must expire within 31 days; however a duration of " + duration.String() + " was specified to (*tcclient.ConnectionData).CreateTemporaryCredentials(...) method")
	}

	now := time.Now()
	start := now.Add(time.Minute * -5) // subtract 5 min for clock drift
	expiry := now.Add(duration)

	if permaCreds.ClientID == "" {
		return nil, errors.New("Temporary credentials cannot be created from credentials that have an empty ClientId")
	}
	if permaCreds.AccessToken == "" {
		return nil, errors.New("Temporary credentials cannot be created from credentials that have an empty AccessToken")
	}
	if permaCreds.Certificate != "" {
		return nil, errors.New("Temporary credentials cannot be created from temporary credentials, only from permanent credentials")
	}

	cert := &Certificate{
		Version:   1,
		Scopes:    scopes,
		Start:     start.UnixNano() / 1e6,
		Expiry:    expiry.UnixNano() / 1e6,
		Seed:      slugid.V4() + slugid.V4(),
		Signature: "", // gets set in updateSignature() method below
	}
	// include the issuer iff this is a named credential
	if tempClientID != "" {
		cert.Issuer = permaCreds.ClientID
	}

	cert.updateSignature(permaCreds.AccessToken, tempClientID)

	certBytes, err := json.Marshal(cert)
	if err != nil {
		return
	}

	tempAccessToken, err := generateTemporaryAccessToken(permaCreds.AccessToken, cert.Seed)
	if err != nil {
		return
	}

	tempCreds = &Credentials{
		ClientID:         permaCreds.ClientID,
		AccessToken:      tempAccessToken,
		Certificate:      string(certBytes),
		AuthorizedScopes: permaCreds.AuthorizedScopes,
	}
	if tempClientID != "" {
		tempCreds.ClientID = tempClientID
	}

	return
}
예제 #2
0
// Test that 10000 v4 slugs are unchanged after decoding and then encoding them.
func TestSlugDecodeEncode(t *testing.T) {
	for i := 0; i < 10000; i++ {
		slug1 := slugid.V4()
		uuid_ := slugid.Decode(slug1)
		slug2 := slugid.Encode(uuid_)
		if slug1 != slug2 {
			t.Errorf("Decode and encode isn't identity: '%s' != '%s'", slug1, slug2)
		}
	}
}
func TestInteractivePluginShell(t *testing.T) {
	taskID := slugid.V4()
	q := &client.MockQueue{}
	shell := q.ExpectRedirectArtifact(taskID, 0, "private/interactive/shell.html")
	sockets := q.ExpectS3Artifact(taskID, 0, "private/interactive/sockets.json")
	plugintest.Case{
		Payload: `{
			"delay": 250,
			"function": "true",
			"argument": "whatever",
			"interactive": {
				"disableDisplay": true
			}
		}`,
		Plugin:        "interactive",
		PluginConfig:  `{}`,
		PluginSuccess: true,
		EngineSuccess: true,
		QueueMock:     q,
		TaskID:        taskID,
		AfterStarted: func(plugintest.Options) {
			shellToolURL := <-shell
			u, _ := url.Parse(shellToolURL)
			shellSocketURL := u.Query().Get("socketUrl")

			// Check that socket.json contains the socket url too
			var s map[string]string
			json.Unmarshal(<-sockets, &s)
			if shellSocketURL != s["shellSocketUrl"] {
				panic("Expected shellSocketUrl to match redirect artifact target")
			}

			debug("Opening a new shell")
			sh, err := shellclient.Dial(shellSocketURL, nil, false)
			if err != nil {
				panic(fmt.Sprintf("Failed to open shell, error: %s", err))
			}

			debug("Write print-hello to shell")
			go func() {
				sh.StdinPipe().Write([]byte("print-hello"))
				sh.StdinPipe().Close()
			}()

			debug("Read message from shell")
			msg, err := ioutil.ReadAll(sh.StdoutPipe())
			if err != nil {
				panic(fmt.Sprintf("Error reading from shell, error: %s", err))
			}
			if string(msg) != "Hello World" {
				panic(fmt.Sprintf("Expected 'Hello World' got: '%s'", string(msg)))
			}

			debug("Wait for shell to terminate")
			result, err := sh.Wait()
			if err != nil {
				panic(fmt.Sprintf("Error from shell, error: %s", err))
			}
			if !result {
				panic("Shell didn't end successfully")
			}
		},
	}.Test()
}
func TestInteractivePluginDisplay(t *testing.T) {
	taskID := slugid.V4()
	q := &client.MockQueue{}
	display := q.ExpectRedirectArtifact(taskID, 0, "private/interactive/display.html")
	sockets := q.ExpectS3Artifact(taskID, 0, "private/interactive/sockets.json")
	plugintest.Case{
		Payload: `{
			"delay": 250,
			"function": "true",
			"argument": "whatever",
			"interactive": {
				"disableShell": true
			}
		}`,
		Plugin:        "interactive",
		PluginConfig:  `{}`,
		PluginSuccess: true,
		EngineSuccess: true,
		QueueMock:     q,
		TaskID:        taskID,
		AfterStarted: func(plugintest.Options) {
			displayToolURL := <-display
			u, _ := url.Parse(displayToolURL)
			displaysURL := u.Query().Get("displaysUrl")
			socketURL := u.Query().Get("socketUrl")

			// Check that socket.json contains the socket url too
			var s map[string]string
			json.Unmarshal(<-sockets, &s)
			if socketURL != s["displaySocketUrl"] {
				panic("Expected displaySocketUrl to match redirect artifact target")
			}
			if displaysURL != s["displaysUrl"] {
				panic("Expected displaySocketUrl to match redirect artifact target")
			}

			debug("List displays")
			displays, err := displayclient.ListDisplays(displaysURL)
			if err != nil {
				panic(fmt.Sprintf("ListDisplays failed, error: %s", err))
			}
			if len(displays) != 1 {
				panic("Expected ListDisplays to return at-least one display")
			}

			debug("OpenDisplay")
			d, err := displays[0].OpenDisplay()
			if err != nil {
				panic(fmt.Sprintf("Failed to OpenDisplay, error: %s", err))
			}

			debug("Get resolution")
			res, err := getDisplayResolution(d)
			if err != nil {
				panic(fmt.Sprintf("Failed connect to VNC display, error: %s", err))
			}

			// Some simple sanity tests, we can rely on the fact that resolution
			// doesn't change because we're testing against mock engine.
			if res.height != displays[0].Height {
				panic("height mismatch")
			}
			if res.width != displays[0].Width {
				panic("width mismatch")
			}
		},
	}.Test()
}
func TestLiveLogStreaming(t *testing.T) {
	taskID := slugid.V4()

	// Create a mock queue
	q := &client.MockQueue{}
	livelog := q.ExpectRedirectArtifact(taskID, 0, "public/logs/live.log")
	backing := q.ExpectS3Artifact(taskID, 0, "public/logs/live_backing.log")

	// Setup test case
	plugintest.Case{
		// We use the ping-proxy payload here. This way the mock-engine won't
		// finish until the proxy replies 200 OK. In the proxy handler we don't
		// reply OK, until we've read 'Pinging' from the livelog. Hence, we ensure
		// that we're able to read a partial livelog.
		Payload: `{
			"delay": 0,
			"function": "ping-proxy",
			"argument": "http://my-proxy/test"
		}`,
		Proxies: map[string]http.Handler{
			"my-proxy": http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				// Get the livelog url
				u := <-livelog
				assert.Contains(t, u, "http", "Expected a redirect URL")

				// Open request to livelog
				req, err := http.NewRequest("GET", u, nil)
				require.NoError(t, err)
				res, err := http.DefaultClient.Do(req)
				require.NoError(t, err)
				defer res.Body.Close()

				// Read 'Pinging' from livelog before continuing
				data := []byte{}
				for err == nil {
					d := []byte{0}
					var n int
					n, err = res.Body.Read(d)
					if n > 0 {
						data = append(data, d[0])
					}
					if strings.Contains(string(data), "Pinging") {
						break
					}
				}

				// Reply so that we can continue
				if strings.Contains(string(data), "Pinging") {
					w.WriteHeader(http.StatusOK)
					w.Write([]byte("[hello-world-yt5aqnur3]"))
				} else {
					w.WriteHeader(400)
					w.Write([]byte("Expected to see 'Pinging'"))
				}
			}),
		},
		Plugin:        "livelog",
		TestStruct:    t,
		PluginSuccess: true,
		EngineSuccess: true,
		MatchLog:      "[hello-world-yt5aqnur3]",
		TaskID:        taskID,
		QueueMock:     q,
		AfterFinished: func(plugintest.Options) {
			assert.Contains(t, <-livelog, taskID, "Expected an artifact URL containing the taskId")
			assert.Contains(t, string(<-backing), "[hello-world-yt5aqnur3]")
		},
	}.Test()
}