harbor/utils/registry/auth/handler.go

198 lines
4.7 KiB
Go
Raw Normal View History

2016-04-13 06:43:17 +00: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"
"io/ioutil"
2016-04-13 06:43:17 +00:00
"net/http"
"net/url"
"strings"
2016-04-15 05:17:32 +00:00
token_util "github.com/vmware/harbor/service/token"
2016-04-13 06:43:17 +00:00
"github.com/vmware/harbor/utils/log"
registry_errors "github.com/vmware/harbor/utils/registry/errors"
2016-04-13 06:43:17 +00:00
)
2016-04-18 10:24:08 +00:00
const (
// credential type
basicAuth string = "basic_auth"
secretKey string = "secret_key"
)
2016-04-13 06:43:17 +00:00
// Handler authorizes the request when encounters a 401 error
type Handler interface {
2016-04-13 07:54:29 +00:00
// Schema : basic, bearer
Schema() string
2016-04-13 06:43:17 +00:00
//AuthorizeRequest adds basic auth or token auth to the header of request
AuthorizeRequest(req *http.Request, params map[string]string) error
}
// Credential ...
2016-04-18 10:24:08 +00:00
type Credential interface {
2016-04-19 02:31:33 +00:00
// AddAuthorization adds authorization information to request
AddAuthorization(req *http.Request)
2016-04-18 10:24:08 +00:00
}
type basicAuthCredential struct {
username string
password string
}
// NewBasicAuthCredential ...
func NewBasicAuthCredential(username, password string) Credential {
return &basicAuthCredential{
username: username,
password: password,
}
}
2016-04-19 02:31:33 +00:00
func (b *basicAuthCredential) AddAuthorization(req *http.Request) {
req.SetBasicAuth(b.username, b.password)
2016-04-13 06:43:17 +00:00
}
type token struct {
Token string `json:"token"`
}
2016-04-13 07:54:29 +00:00
type standardTokenHandler struct {
2016-04-13 06:43:17 +00:00
client *http.Client
2016-04-18 10:24:08 +00:00
credential Credential
2016-04-13 06:43:17 +00:00
}
2016-04-15 05:17:32 +00: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 06:43:17 +00:00
// TODO deal with https
2016-04-18 10:24:08 +00:00
func NewStandardTokenHandler(credential Credential) Handler {
2016-04-13 07:54:29 +00:00
return &standardTokenHandler{
2016-04-13 06:43:17 +00:00
client: &http.Client{
Transport: http.DefaultTransport,
},
credential: credential,
}
}
2016-04-15 05:17:32 +00:00
// Schema implements the corresponding method in interface AuthHandler
2016-04-13 07:54:29 +00:00
func (t *standardTokenHandler) Schema() string {
2016-04-13 06:43:17 +00:00
return "bearer"
}
2016-04-15 05:17:32 +00:00
// AuthorizeRequest implements the corresponding method in interface AuthHandler
2016-04-13 07:54:29 +00:00
func (t *standardTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
2016-04-13 06:43:17 +00: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
}
2016-04-19 02:31:33 +00:00
t.credential.AddAuthorization(r)
2016-04-13 06:43:17 +00:00
resp, err := t.client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
2016-04-13 06:43:17 +00:00
}
if resp.StatusCode != http.StatusOK {
return registry_errors.Error{
StatusCode: resp.StatusCode,
Message: string(b),
}
}
2016-04-13 06:43:17 +00: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 07:54:29 +00:00
log.Debugf("standardTokenHandler generated token successfully | %s %s", req.Method, req.URL)
return nil
}
2016-04-15 05:17:32 +00:00
type usernameTokenHandler struct {
username string
2016-04-13 07:54:29 +00:00
}
2016-04-15 05:17:32 +00: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 07:54:29 +00:00
}
}
2016-04-15 05:17:32 +00:00
// Schema implements the corresponding method in interface AuthHandler
func (u *usernameTokenHandler) Schema() string {
2016-04-13 07:54:29 +00:00
return "bearer"
}
2016-04-15 05:17:32 +00:00
// AuthorizeRequest implements the corresponding method in interface AuthHandler
func (u *usernameTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
2016-04-13 07:54:29 +00:00
service := params["service"]
scopes := []string{}
scope := params["scope"]
if len(scope) != 0 {
scopes = strings.Split(scope, " ")
}
2016-04-15 05:17:32 +00:00
token, err := token_util.GenTokenForUI(u.username, service, scopes)
2016-04-13 07:54:29 +00:00
if err != nil {
return err
}
req.Header.Add(http.CanonicalHeaderKey("Authorization"), fmt.Sprintf("Bearer %s", token))
2016-04-15 05:17:32 +00:00
log.Debugf("usernameTokenHandler generated token successfully | %s %s", req.Method, req.URL)
2016-04-13 06:43:17 +00:00
return nil
}