Issue #2659 - Fix 1.3 login regressions (#2660)

* Issue #2659 - Fix 1.3 login regressions

* Add server.go tests
This commit is contained in:
Alexander Matyushentsev 2019-11-07 14:52:17 -08:00 committed by GitHub
parent 4facca0ae7
commit f4400b9493
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 29 deletions

View file

@ -492,16 +492,17 @@ func (a *ArgoCDServer) translateGrpcCookieHeader(ctx context.Context, w http.Res
}
token := sessionResp.Token
if token != "" {
token, err := zjwt.ZJWT(token)
var err error
token, err = zjwt.ZJWT(token)
if err != nil {
return err
}
cookie, err := httputil.MakeCookieMetadata(common.AuthCookieName, token, flags...)
if err != nil {
return err
}
w.Header().Set("Set-Cookie", cookie)
}
cookie, err := httputil.MakeCookieMetadata(common.AuthCookieName, token, flags...)
if err != nil {
return err
}
w.Header().Set("Set-Cookie", cookie)
}
return nil
}

View file

@ -4,10 +4,13 @@ import (
"context"
"fmt"
"io/ioutil"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/argoproj/argo-cd/pkg/apiclient/session"
"google.golang.org/grpc/metadata"
"github.com/dgrijalva/jwt-go"
@ -479,3 +482,31 @@ func Test_getToken(t *testing.T) {
assert.Equal(t, token, getToken(metadata.New(map[string]string{"grpcgateway-cookie": "argocd.token=" + token})))
})
}
func TestTranslateGrpcCookieHeader(t *testing.T) {
argoCDOpts := ArgoCDServerOpts{
Namespace: test.FakeArgoCDNamespace,
KubeClientset: fake.NewSimpleClientset(test.NewFakeConfigMap(), test.NewFakeSecret()),
AppClientset: apps.NewSimpleClientset(),
}
argocd := NewServer(context.Background(), argoCDOpts)
t.Run("TokenIsNotEmpty", func(t *testing.T) {
recorder := httptest.NewRecorder()
err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{
Token: "xyz",
})
assert.NoError(t, err)
assert.Equal(t, "argocd.token=xyz; path=/; SameSite=lax; httpOnly; Secure", recorder.Result().Header.Get("Set-Cookie"))
})
t.Run("TokenIsEmpty", func(t *testing.T) {
recorder := httptest.NewRecorder()
err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{
Token: "",
})
assert.NoError(t, err)
assert.Equal(t, "argocd.token=; path=/; SameSite=lax; httpOnly; Secure", recorder.Result().Header.Get("Set-Cookie"))
})
}

View file

@ -67,18 +67,25 @@ async function isExpiredSSO() {
requests.onError.subscribe(async err => {
if (err.status === 401) {
if (!history.location.pathname.startsWith('/login')) {
// Query for basehref and remove trailing /.
// If basehref is the default `/` it will become an empty string.
const basehref = document
.querySelector('head > base')
.getAttribute('href')
.replace(/\/$/, '');
if (await isExpiredSSO()) {
window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`;
} else {
history.push(`${basehref}/login?return_url=${encodeURIComponent(location.href)}`);
}
if (history.location.pathname.startsWith('/login')) {
return;
}
const isSSO = await isExpiredSSO();
// location might change after async method call, so we need to check again.
if (history.location.pathname.startsWith('/login')) {
return;
}
// Query for basehref and remove trailing /.
// If basehref is the default `/` it will become an empty string.
const basehref = document
.querySelector('head > base')
.getAttribute('href')
.replace(/\/$/, '');
if (isSSO) {
window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`;
} else {
history.push(`${basehref}/login?return_url=${encodeURIComponent(location.href)}`);
}
}
});

View file

@ -1,5 +1,4 @@
import {DataLoader, Page as ArgoPage, Toolbar, Utils} from 'argo-ui';
import {parse} from 'cookie';
import * as PropTypes from 'prop-types';
import * as React from 'react';
import {Observable} from 'rxjs';
@ -21,16 +20,19 @@ export class Page extends React.Component<{title: string; toolbar?: Toolbar | Ob
toolbar = toolbar || {};
toolbar.tools = [
toolbar.tools,
// this is a crummy check, as the token maybe expired, but it is better than flashing user interface
parse(document.cookie)['argocd.token'] ? (
<a key='logout' onClick={() => this.goToLogin(true)}>
Logout
</a>
) : (
<a key='login' onClick={() => this.goToLogin(false)}>
Login
</a>
)
<DataLoader key='loginPanel' load={() => services.users.get()}>
{info =>
info.loggedIn ? (
<a key='logout' onClick={() => this.goToLogin(true)}>
Logout
</a>
) : (
<a key='login' onClick={() => this.goToLogin(false)}>
Login
</a>
)
}
</DataLoader>
];
return toolbar;
})