Skip to content

Commit

Permalink
Add integration test to check token timeouts
Browse files Browse the repository at this point in the history
NOTE: because we impose a minimum timeout of 5min (to keep writes
limited to a reasonable amount) we need to special case out this
test so that it is allowed to run for more than 5 minutes.

Signed-off-by: Simo Sorce <[email protected]>
  • Loading branch information
simo5 committed Dec 13, 2017
1 parent bf4a3ff commit c8badfe
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 1 deletion.
201 changes: 201 additions & 0 deletions test/integration/oauth_timeout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package integration

import (
"testing"
"time"

kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
restclient "k8s.io/client-go/rest"

"github.com/openshift/origin/pkg/cmd/util/tokencmd"
oauthapi "github.com/openshift/origin/pkg/oauth/apis/oauth"
oauthvalidation "github.com/openshift/origin/pkg/oauth/apis/oauth/validation"
oauthclient "github.com/openshift/origin/pkg/oauth/generated/internalclientset/typed/oauth/internalversion"
userclient "github.com/openshift/origin/pkg/user/generated/internalclientset/typed/user/internalversion"
testutil "github.com/openshift/origin/test/util"
testserver "github.com/openshift/origin/test/util/server"
)

func TestOAuthTimeout(t *testing.T) {
testTimeout := int32(oauthvalidation.ReferenceTimeoutInterval * 2)
masterOptions, err := testserver.DefaultMasterOptions()
if err != nil {
t.Fatal(err)
}
masterOptions.OAuthConfig.TokenConfig.AccessTokenInactivityTimeoutSeconds = &testTimeout
defer testserver.CleanupMasterEtcd(t, masterOptions)

clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterOptions)
if err != nil {
t.Fatal(err)
}

clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig)
if err != nil {
t.Fatal(err)
}
oauthClient := oauthclient.NewForConfigOrDie(clientConfig)

// Use the server and CA info
anonConfig := restclient.AnonymousClientConfig(clientConfig)

{
client, err := oauthClient.OAuthClients().Create(&oauthapi.OAuthClient{
ObjectMeta: metav1.ObjectMeta{Name: "defaulttimeout"},
RespondWithChallenges: true,
RedirectURIs: []string{"http://localhost"},
GrantMethod: oauthapi.GrantHandlerAuto,
})
if err != nil {
t.Fatal(err)
}

testTimeoutOAuthFlows(t, oauthClient.OAuthAccessTokens(), client, anonConfig, 900)
}

{
client, err := oauthClient.OAuthClients().Create(&oauthapi.OAuthClient{
ObjectMeta: metav1.ObjectMeta{Name: "notimeout"},
RespondWithChallenges: true,
RedirectURIs: []string{"http://localhost"},
AccessTokenInactivityTimeoutSeconds: new(int32),
GrantMethod: oauthapi.GrantHandlerAuto,
})
if err != nil {
t.Fatal(err)
}

testTimeoutOAuthFlows(t, oauthClient.OAuthAccessTokens(), client, anonConfig, 0)
}

// check that we get an error trying to set a client value that is less
// than the allowable minimum
{
invalid := int32(oauthvalidation.ReferenceTimeoutInterval - 1)
_, err := oauthClient.OAuthClients().Create(&oauthapi.OAuthClient{
ObjectMeta: metav1.ObjectMeta{Name: "notvalid"},
RespondWithChallenges: true,
RedirectURIs: []string{"http://localhost"},
AccessTokenInactivityTimeoutSeconds: &invalid,
GrantMethod: oauthapi.GrantHandlerAuto,
})
if !kerrors.IsInvalid(err) {
t.Errorf("The 'notvalid' test is supposed to be invalid but gave=%v", err)
}
}

{
min := int32(oauthvalidation.ReferenceTimeoutInterval)
client, err := oauthClient.OAuthClients().Create(&oauthapi.OAuthClient{
ObjectMeta: metav1.ObjectMeta{Name: "mintimeout"},
RespondWithChallenges: true,
RedirectURIs: []string{"http://localhost"},
AccessTokenInactivityTimeoutSeconds: &min,
GrantMethod: oauthapi.GrantHandlerAuto,
})
if err != nil {
t.Fatal(err)
}

token := testTimeoutOAuthFlows(t, oauthClient.OAuthAccessTokens(), client, anonConfig, int(min))

// wait 50% of timeout time, then try token and see it still work
time.Sleep(time.Duration(min/2) * time.Second)
testTokenWorks(t, anonConfig, token, false)

// Then Ensure the token times out
time.Sleep(time.Duration(min+1) * time.Second)
testTokenWorks(t, anonConfig, token, true)
}
}

func testTokenWorks(t *testing.T, anonConfig *restclient.Config, token string, expectTimeout bool) {
// Make sure we can use the token, and it represents who we expect
userConfig := *anonConfig
userConfig.BearerToken = token
userClient, err := userclient.NewForConfig(&userConfig)
if err != nil {
t.Fatal(err)
}

user, err := userClient.Users().Get("~", metav1.GetOptions{})
if err != nil {
if expectTimeout && kerrors.IsUnauthorized(err) {
return
}
t.Errorf("Unexpected error getting user ~: %v", err)
}
if user.Name != "username" {
t.Errorf("Expected username as the user, got %v", user)
}
}

func testTimeoutOAuthFlows(t *testing.T, tokens oauthclient.OAuthAccessTokenInterface, oauthClient *oauthapi.OAuthClient, anonConfig *restclient.Config, expectedTimeout int) string {
var lastToken string

// token flow followed by code flow
for _, tokenFlow := range []bool{true, false} {
tokenOpts := tokencmd.NewRequestTokenOptions(anonConfig, nil, "username", "password", tokenFlow)
if err := tokenOpts.SetDefaultOsinConfig(); err != nil {
t.Fatal(err)
}
tokenOpts.OsinConfig.ClientId = oauthClient.Name
tokenOpts.OsinConfig.RedirectUrl = oauthClient.RedirectURIs[0]
token, err := tokenOpts.RequestToken()
if err != nil {
t.Fatal(err)
}

// Make sure the token exists with the overridden time
tokenObj, err := tokens.Get(token, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
if tokenObj.InactivityTimeoutSeconds != int32(expectedTimeout) {
t.Errorf("Token flow=%v, expected timeout of %d, got %#v",
tokenFlow, expectedTimeout, tokenObj.InactivityTimeoutSeconds)
}

testTokenWorks(t, anonConfig, token, false)

lastToken = token
}

return lastToken
}

func TestOAuthTimeoutNotEnabled(t *testing.T) {
masterConfig, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI()
if err != nil {
t.Fatal(err)
}
defer testserver.CleanupMasterEtcd(t, masterConfig)

clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig)
if err != nil {
t.Fatal(err)
}
oauthClient := oauthclient.NewForConfigOrDie(clientConfig)

// Use the server and CA info
anonConfig := restclient.AnonymousClientConfig(clientConfig)

min := int32(oauthvalidation.ReferenceTimeoutInterval)
client, err := oauthClient.OAuthClients().Create(&oauthapi.OAuthClient{
ObjectMeta: metav1.ObjectMeta{Name: "shorttimeoutthatisignored"},
RespondWithChallenges: true,
RedirectURIs: []string{"http://localhost"},
AccessTokenInactivityTimeoutSeconds: &min,
GrantMethod: oauthapi.GrantHandlerAuto,
})
if err != nil {
t.Fatal(err)
}

token := testTimeoutOAuthFlows(t, oauthClient.OAuthAccessTokens(), client, anonConfig, int(min))

// ensure the token does not timeout because the feature is not active by default
time.Sleep(time.Duration(min+30) * time.Second)
testTokenWorks(t, anonConfig, token, false)
}
9 changes: 8 additions & 1 deletion test/integration/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
)

var timeout = flag.Duration("sub.timeout", 5*time.Minute, "Specify the timeout for each sub test")
var timeoutException = map[string]*time.Duration{
"TestOAuthTimeout": flag.Duration("oauth.timeout", 15*time.Minute, "Timeout for the OAuth test"),
}

func TestIntegration(t *testing.T) {
executeTests(t, "..", "github.com/openshift/origin/test/integration", 1)
Expand Down Expand Up @@ -160,7 +163,11 @@ func runSingleTest(t *testing.T, dir, binaryPath, name string) error {
if testing.Short() {
cmd.Args = append(cmd.Args, "-test.short")
}
cmd.Args = append(cmd.Args, "-test.timeout", (*timeout).String())
if specialTimeout, ok := timeoutException[name]; ok {
cmd.Args = append(cmd.Args, "-test.timeout", (*specialTimeout).String())
} else {
cmd.Args = append(cmd.Args, "-test.timeout", (*timeout).String())
}

out, err := cmd.CombinedOutput()
if err != nil {
Expand Down

0 comments on commit c8badfe

Please sign in to comment.