mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 01:27:49 +01:00
Support getting user info via token in UAA Client (#3686)
This commit is contained in:
parent
30e536b18b
commit
d13321f2b5
@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -30,6 +31,8 @@ import (
|
||||
type Client interface {
|
||||
//PasswordAuth accepts username and password, return a token if it's valid.
|
||||
PasswordAuth(username, password string) (*oauth2.Token, error)
|
||||
//GetUserInfoByToken send the token to OIDC endpoint to get user info, currently it's also used to validate the token.
|
||||
GetUserInfo(token string) (*UserInfo, error)
|
||||
}
|
||||
|
||||
// ClientConfig values to initialize UAA Client
|
||||
@ -42,10 +45,22 @@ type ClientConfig struct {
|
||||
CARootPath string
|
||||
}
|
||||
|
||||
// UserInfo represent the JSON object of a userinfo response from UAA.
|
||||
// As the response varies, this struct will contain only a subset of attributes
|
||||
// that may be used in Harbor
|
||||
type UserInfo struct {
|
||||
UserID string `json:"user_id"`
|
||||
Sub string `json:"sub"`
|
||||
UserName string `json:"user_name"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// DefaultClient leverages oauth2 pacakge for oauth features
|
||||
type defaultClient struct {
|
||||
httpClient *http.Client
|
||||
oauth2Cfg *oauth2.Config
|
||||
endpoint string
|
||||
//TODO: add public key, etc...
|
||||
}
|
||||
|
||||
@ -54,6 +69,29 @@ func (dc *defaultClient) PasswordAuth(username, password string) (*oauth2.Token,
|
||||
return dc.oauth2Cfg.PasswordCredentialsToken(ctx, username, password)
|
||||
}
|
||||
|
||||
func (dc *defaultClient) GetUserInfo(token string) (*UserInfo, error) {
|
||||
userInfoURL := dc.endpoint + "/uaa/userinfo"
|
||||
req, err := http.NewRequest(http.MethodGet, userInfoURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Authorization", "bearer "+token)
|
||||
resp, err := dc.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := &UserInfo{}
|
||||
if err := json.Unmarshal(data, info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// NewDefaultClient creates an instance of defaultClient.
|
||||
func NewDefaultClient(cfg *ClientConfig) (Client, error) {
|
||||
url := cfg.Endpoint
|
||||
@ -95,5 +133,6 @@ func NewDefaultClient(cfg *ClientConfig) (Client, error) {
|
||||
return &defaultClient{
|
||||
httpClient: hc,
|
||||
oauth2Cfg: oc,
|
||||
endpoint: url,
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
package uaa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/src/common/utils/uaa/test"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -43,6 +46,28 @@ func TestPasswordAuth(t *testing.T) {
|
||||
assert.NotNil(err)
|
||||
}
|
||||
|
||||
func TestUserInfo(t *testing.T) {
|
||||
cfg := &ClientConfig{
|
||||
ClientID: "uaa",
|
||||
ClientSecret: "secret",
|
||||
Endpoint: mockUAAServer.URL,
|
||||
SkipTLSVerify: true,
|
||||
}
|
||||
assert := assert.New(t)
|
||||
client, err := NewDefaultClient(cfg)
|
||||
assert.Nil(err)
|
||||
token, err := ioutil.ReadFile(path.Join(currPath(), "test", "./good-access-token.txt"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
userInfo, err := client.GetUserInfo(strings.TrimSpace(string(token)))
|
||||
assert.Nil(err, fmt.Sprintf("%v", err))
|
||||
|
||||
assert.Equal("user01", userInfo.UserName)
|
||||
_, err2 := client.GetUserInfo("bad")
|
||||
assert.NotNil(err2)
|
||||
}
|
||||
|
||||
func currPath() string {
|
||||
_, f, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package test
|
||||
package uaa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -32,3 +32,8 @@ func (fc *FakeClient) PasswordAuth(username, password string) (*oauth2.Token, er
|
||||
}
|
||||
return nil, fmt.Errorf("Invalide username and password")
|
||||
}
|
||||
|
||||
// GetUserInfo ...
|
||||
func (fc *FakeClient) GetUserInfo(token string) (*UserInfo, error) {
|
||||
return nil, nil
|
||||
}
|
1
src/common/utils/uaa/test/good-access-token.txt
Normal file
1
src/common/utils/uaa/test/good-access-token.txt
Normal file
@ -0,0 +1 @@
|
||||
eyJSUzI1NiIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiIyNmRjYjg1YzMzZjU0OGM5ODk2YjI4MDEwN2IyOWM0NiIsInN1YiI6IjlhMTM0ODhmLWYzY2YtNDdhNi05OGYwLTRmZWQyMWY0MzUyMCIsInNjb3BlIjpbIm9wZW5pZCJdLCJjbGllbnRfaWQiOiJrdWJlcm5ldGVzIiwiY2lkIjoia3ViZXJuZXRlcyIsImF6cCI6Imt1YmVybmV0ZXMiLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX2lkIjoiOWExMzQ4OGYtZjNjZi00N2E2LTk4ZjAtNGZlZDIxZjQzNTIwIiwib3JpZ2luIjoibGRhcCIsInVzZXJfbmFtZSI6InVzZXIwMSIsImVtYWlsIjoidXNlcjAxQHVzZXIuZnJvbS5sZGFwLmNmIiwiYXV0aF90aW1lIjoxNTExNDA1NDEwLCJyZXZfc2lnIjoiOGEwYmY5OWQiLCJpYXQiOjE1MTE0MDU0MTAsImV4cCI6MTUxMTQ0ODYxMCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0My91YWEvb2F1dGgvdG9rZW4iLCJ6aWQiOiJ1YWEiLCJhdWQiOlsia3ViZXJuZXRlcyIsIm9wZW5pZCJdfQ.I7VBx_cQoYkotRJ8KdmESAf_xjzp-R44BRz9ngHPUnoqr4rSMin-Ful8wNzEnaYaG56_mrIPuLOb6vXGWW1svRU892GOK9WQRSiFp7O81V7f1bH6JXnIGvyBNl3JOkDB9d5wXn137h9vNKq3Z9TF3jD7oXR_OENS8paclW5EAjmjGvEVIhObMmHCLhsJshTWIoP8AwoP1m9iqak_-t0c99HWaf1AgVUtT2i9Jb63ndJGA6BkOSRH_YxXmM_qtXmk_0kRA5oLDR2UGA4TVXCYp1_8iwQYjvGBVxO24I5jJh_zDYs5YLTFeNzMTPEhAl_Te6NiE91gRXq6KiVk9tTfuA
|
@ -21,6 +21,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MockServerConfig ...
|
||||
@ -72,6 +73,28 @@ func (t *tokenHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
type userInfoHandler struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func (u *userInfoHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
v := req.Header.Get("Authorization")
|
||||
prefix := v[0:7]
|
||||
reqToken := v[7:]
|
||||
if strings.ToLower(prefix) != "bearer " || reqToken != u.token {
|
||||
http.Error(rw, "invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
userInfo, err := ioutil.ReadFile(path.Join(currPath(), "./user-info.json"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err2 := rw.Write(userInfo)
|
||||
if err2 != nil {
|
||||
panic(err2)
|
||||
}
|
||||
}
|
||||
|
||||
// NewMockServer ...
|
||||
func NewMockServer(cfg *MockServerConfig) *httptest.Server {
|
||||
mux := http.NewServeMux()
|
||||
@ -81,5 +104,10 @@ func NewMockServer(cfg *MockServerConfig) *httptest.Server {
|
||||
cfg.Username,
|
||||
cfg.Password,
|
||||
})
|
||||
token, err := ioutil.ReadFile(path.Join(currPath(), "./good-access-token.txt"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mux.Handle("/uaa/userinfo", &userInfoHandler{strings.TrimSpace(string(token))})
|
||||
return httptest.NewTLSServer(mux)
|
||||
}
|
||||
|
11
src/common/utils/uaa/test/user-info.json
Normal file
11
src/common/utils/uaa/test/user-info.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"user_id": "9a13488f-f3cf-47a6-98f0-4fed21f43520",
|
||||
"sub": "9a13488f-f3cf-47a6-98f0-4fed21f43520",
|
||||
"user_name": "user01",
|
||||
"given_name": null,
|
||||
"family_name": null,
|
||||
"email": "user01@user.from.ldap.cf",
|
||||
"phone_number": null,
|
||||
"previous_logon_time": 1511247236160,
|
||||
"name": ""
|
||||
}
|
@ -18,7 +18,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
utilstest "github.com/vmware/harbor/src/common/utils/test"
|
||||
uaatest "github.com/vmware/harbor/src/common/utils/uaa/test"
|
||||
"github.com/vmware/harbor/src/common/utils/uaa"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
|
||||
"os"
|
||||
@ -47,7 +47,7 @@ func TestGetClient(t *testing.T) {
|
||||
|
||||
func TestDoAuth(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
client := &uaatest.FakeClient{
|
||||
client := &uaa.FakeClient{
|
||||
Username: "user1",
|
||||
Password: "password1",
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user