2016-04-13 08:43:17 +02:00
|
|
|
/*
|
|
|
|
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2016-04-15 10:51:46 +02:00
|
|
|
"io/ioutil"
|
2016-04-13 08:43:17 +02:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
token_util "github.com/vmware/harbor/service/token"
|
2016-04-13 08:43:17 +02:00
|
|
|
"github.com/vmware/harbor/utils/log"
|
2016-04-15 11:01:59 +02:00
|
|
|
registry_errors "github.com/vmware/harbor/utils/registry/errors"
|
2016-04-13 08:43:17 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Handler authorizes the request when encounters a 401 error
|
|
|
|
type Handler interface {
|
2016-04-13 09:54:29 +02:00
|
|
|
// Schema : basic, bearer
|
|
|
|
Schema() string
|
2016-04-13 08:43:17 +02:00
|
|
|
//AuthorizeRequest adds basic auth or token auth to the header of request
|
|
|
|
AuthorizeRequest(req *http.Request, params map[string]string) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Credential ...
|
|
|
|
type Credential struct {
|
|
|
|
// Username ...
|
|
|
|
Username string
|
|
|
|
// Password ...
|
|
|
|
Password string
|
2016-04-13 09:54:29 +02:00
|
|
|
// SecretKey ...
|
2016-04-13 08:43:17 +02:00
|
|
|
SecretKey string
|
|
|
|
}
|
|
|
|
|
|
|
|
type token struct {
|
|
|
|
Token string `json:"token"`
|
|
|
|
}
|
|
|
|
|
2016-04-13 09:54:29 +02:00
|
|
|
type standardTokenHandler struct {
|
2016-04-13 08:43:17 +02:00
|
|
|
client *http.Client
|
|
|
|
credential *Credential
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
// NewStandardTokenHandler returns a standard token handler. The handler will request a token
|
|
|
|
// from token server whose URL is specified in the "WWW-authentication" header and add it to
|
|
|
|
// the origin request
|
2016-04-13 08:43:17 +02:00
|
|
|
// TODO deal with https
|
2016-04-13 09:54:29 +02:00
|
|
|
func NewStandardTokenHandler(credential *Credential) Handler {
|
|
|
|
return &standardTokenHandler{
|
2016-04-13 08:43:17 +02:00
|
|
|
client: &http.Client{
|
|
|
|
Transport: http.DefaultTransport,
|
|
|
|
},
|
|
|
|
credential: credential,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
// Schema implements the corresponding method in interface AuthHandler
|
2016-04-13 09:54:29 +02:00
|
|
|
func (t *standardTokenHandler) Schema() string {
|
2016-04-13 08:43:17 +02:00
|
|
|
return "bearer"
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
// AuthorizeRequest implements the corresponding method in interface AuthHandler
|
2016-04-13 09:54:29 +02:00
|
|
|
func (t *standardTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
2016-04-13 08:43:17 +02:00
|
|
|
realm, ok := params["realm"]
|
|
|
|
if !ok {
|
|
|
|
return errors.New("no realm")
|
|
|
|
}
|
|
|
|
|
|
|
|
service := params["service"]
|
|
|
|
scope := params["scope"]
|
|
|
|
|
|
|
|
u, err := url.Parse(realm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
q := u.Query()
|
|
|
|
q.Add("service", service)
|
|
|
|
|
|
|
|
for _, s := range strings.Split(scope, " ") {
|
|
|
|
q.Add("scope", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
u.RawQuery = q.Encode()
|
|
|
|
|
|
|
|
r, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO support secretKey
|
2016-04-15 07:17:32 +02:00
|
|
|
r.SetBasicAuth(t.credential.Username, t.credential.Password)
|
2016-04-13 08:43:17 +02:00
|
|
|
|
|
|
|
resp, err := t.client.Do(r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-04-15 10:51:46 +02:00
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-04-13 08:43:17 +02:00
|
|
|
}
|
|
|
|
|
2016-04-15 10:51:46 +02:00
|
|
|
if resp.StatusCode != http.StatusOK {
|
2016-04-15 11:01:59 +02:00
|
|
|
return registry_errors.Error{
|
2016-04-15 10:51:46 +02:00
|
|
|
StatusCode: resp.StatusCode,
|
|
|
|
Message: string(b),
|
|
|
|
}
|
|
|
|
}
|
2016-04-13 08:43:17 +02:00
|
|
|
|
|
|
|
decoder := json.NewDecoder(resp.Body)
|
|
|
|
|
|
|
|
tk := &token{}
|
|
|
|
if err = decoder.Decode(tk); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Add(http.CanonicalHeaderKey("Authorization"), fmt.Sprintf("Bearer %s", tk.Token))
|
|
|
|
|
2016-04-13 09:54:29 +02:00
|
|
|
log.Debugf("standardTokenHandler generated token successfully | %s %s", req.Method, req.URL)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
type usernameTokenHandler struct {
|
|
|
|
username string
|
2016-04-13 09:54:29 +02:00
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
// NewUsernameTokenHandler returns a handler which will generate
|
|
|
|
// a token according the user's privileges
|
|
|
|
func NewUsernameTokenHandler(username string) Handler {
|
|
|
|
return &usernameTokenHandler{
|
|
|
|
username: username,
|
2016-04-13 09:54:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
// Schema implements the corresponding method in interface AuthHandler
|
|
|
|
func (u *usernameTokenHandler) Schema() string {
|
2016-04-13 09:54:29 +02:00
|
|
|
return "bearer"
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
// AuthorizeRequest implements the corresponding method in interface AuthHandler
|
|
|
|
func (u *usernameTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
2016-04-13 09:54:29 +02:00
|
|
|
service := params["service"]
|
|
|
|
|
|
|
|
scopes := []string{}
|
|
|
|
scope := params["scope"]
|
|
|
|
if len(scope) != 0 {
|
|
|
|
scopes = strings.Split(scope, " ")
|
|
|
|
}
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
token, err := token_util.GenTokenForUI(u.username, service, scopes)
|
2016-04-13 09:54:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Add(http.CanonicalHeaderKey("Authorization"), fmt.Sprintf("Bearer %s", token))
|
|
|
|
|
2016-04-15 07:17:32 +02:00
|
|
|
log.Debugf("usernameTokenHandler generated token successfully | %s %s", req.Method, req.URL)
|
2016-04-13 08:43:17 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|