Esempio n. 1
0
func TestSillyAccessController(t *testing.T) {
	ac := &accessController{
		realm:   "test-realm",
		service: "test-service",
	}

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithRequest(context.Background(), r)
		authCtx, err := ac.Authorized(ctx)
		if err != nil {
			switch err := err.(type) {
			case auth.Challenge:
				err.SetHeaders(w)
				w.WriteHeader(http.StatusUnauthorized)
				return
			default:
				t.Fatalf("unexpected error authorizing request: %v", err)
			}
		}

		userInfo, ok := authCtx.Value(auth.UserKey).(auth.UserInfo)
		if !ok {
			t.Fatal("silly accessController did not set auth.user context")
		}

		if userInfo.Name != "silly" {
			t.Fatalf("expected user name %q, got %q", "silly", userInfo.Name)
		}

		w.WriteHeader(http.StatusNoContent)
	}))

	resp, err := http.Get(server.URL)
	if err != nil {
		t.Fatalf("unexpected error during GET: %v", err)
	}
	defer resp.Body.Close()

	// Request should not be authorized
	if resp.StatusCode != http.StatusUnauthorized {
		t.Fatalf("unexpected response status: %v != %v", resp.StatusCode, http.StatusUnauthorized)
	}

	req, err := http.NewRequest("GET", server.URL, nil)
	if err != nil {
		t.Fatalf("unexpected error creating new request: %v", err)
	}
	req.Header.Set("Authorization", "seriously, anything")

	resp, err = http.DefaultClient.Do(req)
	if err != nil {
		t.Fatalf("unexpected error during GET: %v", err)
	}
	defer resp.Body.Close()

	// Request should not be authorized
	if resp.StatusCode != http.StatusNoContent {
		t.Fatalf("unexpected response status: %v != %v", resp.StatusCode, http.StatusNoContent)
	}
}
Esempio n. 2
0
// ServeHTTP serves an HTTP request and implements the http.Handler interface.
func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ctx := ctxu.WithRequest(root.context, r)
	ctx, w = ctxu.WithResponseWriter(ctx, w)
	ctx = ctxu.WithLogger(ctx, ctxu.GetRequestLogger(ctx))
	ctx = context.WithValue(ctx, "repo", vars["imageName"])
	ctx = context.WithValue(ctx, "cryptoService", root.trust)

	defer func() {
		ctxu.GetResponseLogger(ctx).Info("response completed")
	}()

	if root.auth != nil {
		access := buildAccessRecords(vars["imageName"], root.actions...)
		var authCtx context.Context
		var err error
		if authCtx, err = root.auth.Authorized(ctx, access...); err != nil {
			if err, ok := err.(auth.Challenge); ok {
				err.ServeHTTP(w, r)
				w.WriteHeader(http.StatusUnauthorized)
				return
			}
			errcode.ServeJSON(w, v2.ErrorCodeUnauthorized)
			return
		}
		ctx = authCtx
	}
	if err := root.handler(ctx, w, r); err != nil {
		e := errcode.ServeJSON(w, err)
		if e != nil {
			logrus.Error(e)
		}
		return
	}
}
Esempio n. 3
0
// ServeHTTP serves an HTTP request and implements the http.Handler interface.
func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		err  error
		ctx  = ctxu.WithRequest(root.context, r)
		log  = ctxu.GetRequestLogger(ctx)
		vars = mux.Vars(r)
	)
	ctx, w = ctxu.WithResponseWriter(ctx, w)
	ctx = ctxu.WithLogger(ctx, log)
	ctx = context.WithValue(ctx, notary.CtxKeyCryptoSvc, root.trust)

	defer func() {
		ctxu.GetResponseLogger(ctx).Info("response completed")
	}()

	if root.auth != nil {
		ctx = context.WithValue(ctx, notary.CtxKeyRepo, vars["imageName"])
		if ctx, err = root.doAuth(ctx, vars["imageName"], w); err != nil {
			// errors have already been logged/output to w inside doAuth
			// just return
			return
		}
	}
	if err := root.handler(ctx, w, r); err != nil {
		serveError(log, w, err)
	}
}
Esempio n. 4
0
// handlerWithContext wraps the given context-aware handler by setting up the
// request context from a base context.
func handlerWithContext(ctx context.Context, handler func(context.Context, http.ResponseWriter, *http.Request)) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithRequest(ctx, r)
		logger := context.GetRequestLogger(ctx)
		ctx = context.WithLogger(ctx, logger)

		handler(ctx, w, r)
	})
}
Esempio n. 5
0
// ServeHTTP serves an HTTP request and implements the http.Handler interface.
func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ctx := ctxu.WithRequest(root.context, r)
	log := ctxu.GetRequestLogger(ctx)
	ctx, w = ctxu.WithResponseWriter(ctx, w)
	ctx = ctxu.WithLogger(ctx, log)
	ctx = context.WithValue(ctx, "repo", vars["imageName"])
	ctx = context.WithValue(ctx, "cryptoService", root.trust)

	defer func() {
		ctxu.GetResponseLogger(ctx).Info("response completed")
	}()

	if root.auth != nil {
		access := buildAccessRecords(vars["imageName"], root.actions...)
		var authCtx context.Context
		var err error
		if authCtx, err = root.auth.Authorized(ctx, access...); err != nil {
			if challenge, ok := err.(auth.Challenge); ok {
				// Let the challenge write the response.
				challenge.SetHeaders(w)

				if err := errcode.ServeJSON(w, errcode.ErrorCodeUnauthorized.WithDetail(access)); err != nil {
					log.Errorf("failed to serve challenge response: %s", err.Error())
				}
				return
			}
			errcode.ServeJSON(w, errcode.ErrorCodeUnauthorized)
			return
		}
		ctx = authCtx
	}
	if err := root.handler(ctx, w, r); err != nil {
		if httpErr, ok := err.(errcode.ErrorCoder); ok {
			// info level logging for non-5XX http errors
			httpErrCode := httpErr.ErrorCode().Descriptor().HTTPStatusCode
			if httpErrCode >= http.StatusInternalServerError {
				// error level logging for 5XX http errors
				log.Error(httpErr)
			} else {
				log.Info(httpErr)
			}
		}
		e := errcode.ServeJSON(w, err)
		if e != nil {
			log.Error(e)
		}
		return
	}
}
Esempio n. 6
0
// context either returns a new context or looks it up in the manager.
func (cm *contextManager) context(parent context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	ctx, ok := cm.contexts[r]
	if ok {
		return ctx
	}

	if parent == nil {
		parent = ctxu.Background()
	}

	ctx = ctxu.WithRequest(parent, r)
	ctx, w = ctxu.WithResponseWriter(ctx, w)
	ctx = ctxu.WithLogger(ctx, ctxu.GetRequestLogger(ctx))
	cm.contexts[r] = ctx

	return ctx
}
Esempio n. 7
0
func (t *tokenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := context.WithRequest(t.ctx, req)

	// If no authorization is provided, return a token the auth provider will treat as an anonymous user
	if len(req.Header.Get("Authorization")) == 0 {
		context.GetRequestLogger(ctx).Debugf("anonymous token request")
		t.writeToken(anonymousToken, w, req)
		return
	}

	// use the password as the token
	_, token, ok := req.BasicAuth()
	if !ok {
		context.GetRequestLogger(ctx).Debugf("no basic auth credentials provided")
		t.writeUnauthorized(w, req)
		return
	}

	// TODO: if this doesn't validate as an API token, attempt to obtain an API token using the given username/password
	copied := t.anonymousConfig
	copied.BearerToken = token
	osClient, err := client.New(&copied)
	if err != nil {
		context.GetRequestLogger(ctx).Errorf("error building client: %v", err)
		t.writeError(w, req)
		return
	}

	if _, err := osClient.Users().Get("~"); err != nil {
		context.GetRequestLogger(ctx).Debugf("invalid token: %v", err)
		t.writeUnauthorized(w, req)
		return
	}

	t.writeToken(token, w, req)
}
Esempio n. 8
0
func TestBasicAccessController(t *testing.T) {
	testRealm := "The-Shire"
	testUsers := []string{"bilbo", "frodo", "MiShil", "DeokMan"}
	testPasswords := []string{"baggins", "baggins", "새주", "공주님"}
	testHtpasswdContent := `bilbo:{SHA}5siv5c0SHx681xU6GiSx9ZQryqs=
							frodo:$2y$05$926C3y10Quzn/LnqQH86VOEVh/18T6RnLaS.khre96jLNL/7e.K5W
							MiShil:$2y$05$0oHgwMehvoe8iAWS8I.7l.KoECXrwVaC16RPfaSCU5eVTFrATuMI2
							DeokMan:공주님`

	tempFile, err := ioutil.TempFile("", "htpasswd-test")
	if err != nil {
		t.Fatal("could not create temporary htpasswd file")
	}
	if _, err = tempFile.WriteString(testHtpasswdContent); err != nil {
		t.Fatal("could not write temporary htpasswd file")
	}

	options := map[string]interface{}{
		"realm": testRealm,
		"path":  tempFile.Name(),
	}
	ctx := context.Background()

	accessController, err := newAccessController(options)
	if err != nil {
		t.Fatal("error creating access controller")
	}

	tempFile.Close()

	var userNumber = 0

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithRequest(ctx, r)
		authCtx, err := accessController.Authorized(ctx)
		if err != nil {
			switch err := err.(type) {
			case auth.Challenge:
				err.SetHeaders(w)
				w.WriteHeader(http.StatusUnauthorized)
				return
			default:
				t.Fatalf("unexpected error authorizing request: %v", err)
			}
		}

		userInfo, ok := authCtx.Value(auth.UserKey).(auth.UserInfo)
		if !ok {
			t.Fatal("basic accessController did not set auth.user context")
		}

		if userInfo.Name != testUsers[userNumber] {
			t.Fatalf("expected user name %q, got %q", testUsers[userNumber], userInfo.Name)
		}

		w.WriteHeader(http.StatusNoContent)
	}))

	client := &http.Client{
		CheckRedirect: nil,
	}

	req, _ := http.NewRequest("GET", server.URL, nil)
	resp, err := client.Do(req)

	if err != nil {
		t.Fatalf("unexpected error during GET: %v", err)
	}
	defer resp.Body.Close()

	// Request should not be authorized
	if resp.StatusCode != http.StatusUnauthorized {
		t.Fatalf("unexpected non-fail response status: %v != %v", resp.StatusCode, http.StatusUnauthorized)
	}

	nonbcrypt := map[string]struct{}{
		"bilbo":   {},
		"DeokMan": {},
	}

	for i := 0; i < len(testUsers); i++ {
		userNumber = i
		req, err := http.NewRequest("GET", server.URL, nil)
		if err != nil {
			t.Fatalf("error allocating new request: %v", err)
		}

		req.SetBasicAuth(testUsers[i], testPasswords[i])

		resp, err = client.Do(req)
		if err != nil {
			t.Fatalf("unexpected error during GET: %v", err)
		}
		defer resp.Body.Close()

		if _, ok := nonbcrypt[testUsers[i]]; ok {
			// these are not allowed.
			// Request should be authorized
			if resp.StatusCode != http.StatusUnauthorized {
				t.Fatalf("unexpected non-success response status: %v != %v for %s %s", resp.StatusCode, http.StatusUnauthorized, testUsers[i], testPasswords[i])
			}
		} else {
			// Request should be authorized
			if resp.StatusCode != http.StatusNoContent {
				t.Fatalf("unexpected non-success response status: %v != %v for %s %s", resp.StatusCode, http.StatusNoContent, testUsers[i], testPasswords[i])
			}
		}
	}

}
Esempio n. 9
0
// TestAccessController tests complete integration of the token auth package.
// It starts by mocking the options for a token auth accessController which
// it creates. It then tries a few mock requests:
// 		- don't supply a token; should error with challenge
//		- supply an invalid token; should error with challenge
// 		- supply a token with insufficient access; should error with challenge
//		- supply a valid token; should not error
func TestAccessController(t *testing.T) {
	// Make 2 keys; only the first is to be a trusted root key.
	rootKeys, err := makeRootKeys(2)
	if err != nil {
		t.Fatal(err)
	}

	rootCertBundleFilename, err := writeTempRootCerts(rootKeys[:1])
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(rootCertBundleFilename)

	realm := "https://auth.example.com/token/"
	issuer := "test-issuer.example.com"
	service := "test-service.example.com"

	options := map[string]interface{}{
		"realm":          realm,
		"issuer":         issuer,
		"service":        service,
		"rootcertbundle": rootCertBundleFilename,
	}

	accessController, err := newAccessController(options)
	if err != nil {
		t.Fatal(err)
	}

	// 1. Make a mock http.Request with no token.
	req, err := http.NewRequest("GET", "http://example.com/foo", nil)
	if err != nil {
		t.Fatal(err)
	}

	testAccess := auth.Access{
		Resource: auth.Resource{
			Type: "foo",
			Name: "bar",
		},
		Action: "baz",
	}

	ctx := context.WithRequest(context.Background(), req)
	authCtx, err := accessController.Authorized(ctx, testAccess)
	challenge, ok := err.(auth.Challenge)
	if !ok {
		t.Fatal("accessController did not return a challenge")
	}

	if challenge.Error() != ErrTokenRequired.Error() {
		t.Fatalf("accessControler did not get expected error - got %s - expected %s", challenge, ErrTokenRequired)
	}

	if authCtx != nil {
		t.Fatalf("expected nil auth context but got %s", authCtx)
	}

	// 2. Supply an invalid token.
	token, err := makeTestToken(
		issuer, service,
		[]*ResourceActions{{
			Type:    testAccess.Type,
			Name:    testAccess.Name,
			Actions: []string{testAccess.Action},
		}},
		rootKeys[1], 1, time.Now(), time.Now().Add(5*time.Minute), // Everything is valid except the key which signed it.
	)
	if err != nil {
		t.Fatal(err)
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw()))

	authCtx, err = accessController.Authorized(ctx, testAccess)
	challenge, ok = err.(auth.Challenge)
	if !ok {
		t.Fatal("accessController did not return a challenge")
	}
	if !strings.Contains(challenge.Error(), "could not verify JWK certificate chain") {
		t.Fatalf("accessControler did not get expected error - got %s - expected %s", challenge.Error(), "could not verify JWK certificate chain")
	}

	if authCtx != nil {
		t.Fatalf("expected nil auth context but got %s", authCtx)
	}

	// 3. Supply a token with insufficient access.
	token, err = makeTestToken(
		issuer, service,
		[]*ResourceActions{}, // No access specified.
		rootKeys[0], 1, time.Now(), time.Now().Add(5*time.Minute),
	)
	if err != nil {
		t.Fatal(err)
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw()))

	authCtx, err = accessController.Authorized(ctx, testAccess)
	challenge, ok = err.(auth.Challenge)
	if !ok {
		t.Fatal("accessController did not return a challenge")
	}

	if challenge.Error() != ErrInsufficientScope.Error() {
		t.Fatalf("accessControler did not get expected error - got %s - expected %s", challenge, ErrInsufficientScope)
	}

	if authCtx != nil {
		t.Fatalf("expected nil auth context but got %s", authCtx)
	}

	// 4. Supply the token we need, or deserve, or whatever.
	token, err = makeTestToken(
		issuer, service,
		[]*ResourceActions{{
			Type:    testAccess.Type,
			Name:    testAccess.Name,
			Actions: []string{testAccess.Action},
		}},
		rootKeys[0], 1, time.Now(), time.Now().Add(5*time.Minute),
	)
	if err != nil {
		t.Fatal(err)
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.compactRaw()))

	authCtx, err = accessController.Authorized(ctx, testAccess)
	if err != nil {
		t.Fatalf("accessController returned unexpected error: %s", err)
	}

	userInfo, ok := authCtx.Value(auth.UserKey).(auth.UserInfo)
	if !ok {
		t.Fatal("token accessController did not set auth.user context")
	}

	if userInfo.Name != "foo" {
		t.Fatalf("expected user name %q, got %q", "foo", userInfo.Name)
	}
}