Skip to content

Commit

Permalink
Add principal fallback for authn
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Cieplak committed Jul 19, 2018
1 parent 4de33af commit 8fba487
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 24 deletions.
2 changes: 1 addition & 1 deletion api.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type AthenzPrincipal struct {
type UserMapper interface {
// MapUser maps an Athenz principal to a user info object.
// Returning an error will cause an authentication failure.
MapUser(ctx context.Context, t *zmssvctoken.NToken) (authn.UserInfo, error)
MapUser(ctx context.Context, domain, service string) (authn.UserInfo, error)
}

// AthenzAccessCheck encapsulates the parameters for an authz check against Athenz.
Expand Down
4 changes: 1 addition & 3 deletions api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (

authn "k8s.io/api/authentication/v1beta1"
authz "k8s.io/api/authorization/v1beta1"

"github.com/yahoo/athenz/libs/go/zmssvctoken"
)

func TestAPIAccessCheckString(t *testing.T) {
Expand Down Expand Up @@ -47,7 +45,7 @@ func ensurePanic(t *testing.T, fn func()) {
type urm struct {
}

func (u *urm) MapUser(ctx context.Context, token *zmssvctoken.NToken) (authn.UserInfo, error) {
func (u *urm) MapUser(ctx context.Context, domain, service string) (authn.UserInfo, error) {
return authn.UserInfo{}, errors.New("not implemented")
}

Expand Down
16 changes: 14 additions & 2 deletions authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strings"

authn "k8s.io/api/authentication/v1beta1"
)
Expand Down Expand Up @@ -78,15 +79,26 @@ func (a *authenticator) authenticate(ctx context.Context, nt *ntoken) (ts *authn
}
}

var u authn.UserInfo

token, err := a.Validator.Validate(nt.raw)
if err != nil {
if err != nil && strings.Contains(err.Error(), "Unable to get public key from ZTS") {
client := newClient(a.ZMSEndpoint, a.ZTSEndpoint, a.Timeout, xp)
p, err := client.authenticate()
if err != nil {
return a.deny(err)
}
u, err = a.Mapper.MapUser(ctx, p.Domain, p.Service)
} else if err != nil {
return a.deny(err)
} else {
u, err = a.Mapper.MapUser(ctx, token.Domain, token.Name)
}

u, err := a.Mapper.MapUser(ctx, token)
if err != nil {
return a.deny(err)
}

return &authn.TokenReviewStatus{
Authenticated: true,
User: u,
Expand Down
14 changes: 7 additions & 7 deletions authn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ func getToken(t *testing.T) string {
return token
}

type mpfn func(ctx context.Context, token *zmssvctoken.NToken) (authn.UserInfo, error)
type mpfn func(ctx context.Context, domain, service string) (authn.UserInfo, error)

func (m mpfn) MapUser(ctx context.Context, token *zmssvctoken.NToken) (authn.UserInfo, error) {
return m(ctx, token)
func (m mpfn) MapUser(ctx context.Context, domain, service string) (authn.UserInfo, error) {
return m(ctx, domain, service)
}

type authnScaffold struct {
Expand Down Expand Up @@ -103,9 +103,9 @@ func newAuthnScaffold(t *testing.T) *authnScaffold {
Timeout: 200 * time.Millisecond,
LogProvider: p,
},
Mapper: mpfn(func(ctx context.Context, token *zmssvctoken.NToken) (authn.UserInfo, error) {
Mapper: mpfn(func(ctx context.Context, domain, service string) (authn.UserInfo, error) {
return authn.UserInfo{
Username: token.Domain + "." + token.Name,
Username: domain + "." + service,
UID: "100",
Groups: []string{"foo"},
}, nil
Expand Down Expand Up @@ -239,7 +239,7 @@ func TestAuthnZMSReject(t *testing.T) {
if tr.Status.Authenticated {
t.Error("ZMS reject returned success auth!")
}
msg := "ZTS returned status 401"
msg := "/principal returned 401 (Forbidden)"
if !strings.Contains(tr.Status.Error, msg) {
t.Errorf("status log '%s' did not contain '%s'", tr.Status.Error, msg)
}
Expand Down Expand Up @@ -320,7 +320,7 @@ func TestAuthnBadInputs(t *testing.T) {
type errum struct {
}

func (e *errum) MapUser(ctx context.Context, token *zmssvctoken.NToken) (authn.UserInfo, error) {
func (e *errum) MapUser(ctx context.Context, domain, service string) (authn.UserInfo, error) {
return authn.UserInfo{}, errors.New("FOOBAR")
}

Expand Down
17 changes: 17 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,23 @@ func (c *client) request(u string, data interface{}, validator func(body []byte)
return nil
}

// authenticate make a request assuming that the transport has been configured
// to present the user's token and returns the response from Athenz.
func (c *client) authenticate() (*AthenzPrincipal, error) {
u := fmt.Sprintf("%s/principal", c.zmsEndpoint)
var ap AthenzPrincipal
err := c.request(u, &ap, func(b []byte) error {
if ap.Domain == "" || ap.Service == "" {
return fmt.Errorf("GET %s unable to get domain and/or name from %s", u, b)
}
return nil
})
if err != nil {
return nil, err
}
return &ap, nil
}

// authorize returns true if the supplied principal has access to the resource and action. The initial check is done
// against the zts endpoint. If that is unreachable, the check is retried against the zms endpoint.
func (c *client) authorize(principal string, check AthenzAccessCheck) (bool, error) {
Expand Down
6 changes: 2 additions & 4 deletions example/auth-webhook/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import (
api "github.com/yahoo/k8s-athenz-webhook"
authn "k8s.io/api/authentication/v1beta1"
authz "k8s.io/api/authorization/v1beta1"

"github.com/yahoo/athenz/libs/go/zmssvctoken"
)

// this file contains implementations for the user mapper,
Expand All @@ -32,8 +30,8 @@ type UserMapper struct {
}

// MapUser implements the UserMapper interface as documented for the type.
func (d *UserMapper) MapUser(ctx context.Context, token *zmssvctoken.NToken) (authn.UserInfo, error) {
principal := fmt.Sprintf("%s.%s", token.Domain, token.Name)
func (d *UserMapper) MapUser(ctx context.Context, domain, service string) (authn.UserInfo, error) {
principal := fmt.Sprintf("%s.%s", domain, service)
return authn.UserInfo{
Username: principal,
UID: principal,
Expand Down
8 changes: 1 addition & 7 deletions example/auth-webhook/mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import (
api "github.com/yahoo/k8s-athenz-webhook"
authn "k8s.io/api/authentication/v1beta1"
authz "k8s.io/api/authorization/v1beta1"

"github.com/yahoo/athenz/libs/go/zmssvctoken"
)

var testContext = context.Background()
Expand Down Expand Up @@ -55,11 +53,7 @@ func TestMapSystemNs(t *testing.T) {

func TestMapUserMapping(t *testing.T) {
m := &UserMapper{Groups: []string{"foo"}}
token := &zmssvctoken.NToken{
Domain: "my.domain",
Name: "my-service",
}
u, err := m.MapUser(testContext, token)
u, err := m.MapUser(testContext, "my.domain", "my-service")
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 8fba487

Please sign in to comment.