github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/apiauth/apiauth_test.go (about)

     1  // +build !windows
     2  
     3  package apiauth
     4  
     5  import (
     6  	"context"
     7  	"crypto/tls"
     8  	"fmt"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/golang/mock/gomock"
    16  	. "github.com/smartystreets/goconvey/convey"
    17  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    18  	triremecommon "go.aporeto.io/enforcerd/trireme-lib/common"
    19  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/applicationproxy/serviceregistry"
    20  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext"
    21  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets"
    22  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets/testhelper"
    23  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/servicetokens"
    24  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/usertokens/mockusertokens"
    25  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    26  	"go.aporeto.io/enforcerd/trireme-lib/utils/portspec"
    27  )
    28  
    29  const (
    30  	policyID        = "somepolicy"
    31  	rejectPolicyID  = "somerejectepolicy"
    32  	serviceID       = "someservice"
    33  	rejectServiceID = "somerejectservice"
    34  	namespace       = "somenamespace"
    35  	appLabel        = "app=web"
    36  )
    37  
    38  func newBaseApplicationServices(ctrl *gomock.Controller, id string, ipAddr string, exposedPortValue, publicPortValue, privatePortValue uint16, external bool) *policy.ApplicationService {
    39  
    40  	exposedPort, err := portspec.NewPortSpec(exposedPortValue, exposedPortValue, nil)
    41  	So(err, ShouldBeNil)
    42  	publicPort, err := portspec.NewPortSpec(publicPortValue, publicPortValue, nil)
    43  	So(err, ShouldBeNil)
    44  	privatePort, err := portspec.NewPortSpec(privatePortValue, privatePortValue, nil)
    45  	So(err, ShouldBeNil)
    46  
    47  	return &policy.ApplicationService{
    48  		ID: id,
    49  		NetworkInfo: &triremecommon.Service{
    50  			Ports:     exposedPort,
    51  			Protocol:  6,
    52  			Addresses: map[string]struct{}{ipAddr: struct{}{}},
    53  		},
    54  		PublicNetworkInfo: &triremecommon.Service{
    55  			Ports:     publicPort,
    56  			Protocol:  6,
    57  			Addresses: map[string]struct{}{ipAddr: struct{}{}},
    58  		},
    59  		PrivateNetworkInfo: &triremecommon.Service{
    60  			Ports:     privatePort,
    61  			Protocol:  6,
    62  			Addresses: map[string]struct{}{},
    63  		},
    64  		Type:                 policy.ServiceHTTP,
    65  		PublicServiceTLSType: policy.ServiceTLSTypeAporeto,
    66  		External:             external,
    67  		HTTPRules: []*policy.HTTPRule{
    68  			{
    69  				URIs:    []string{"/admin"},
    70  				Methods: []string{"GET"},
    71  				ClaimMatchingRules: [][]string{
    72  					{appLabel},
    73  				},
    74  				Public: false,
    75  			},
    76  			{
    77  				URIs:    []string{"/public"},
    78  				Methods: []string{"GET"},
    79  				Public:  true,
    80  			},
    81  			{
    82  				URIs:    []string{"/forbidden"},
    83  				Methods: []string{"GET"},
    84  				ClaimMatchingRules: [][]string{
    85  					{"Nobody"},
    86  				},
    87  				Public: false,
    88  			},
    89  		},
    90  		UserAuthorizationType:    policy.UserAuthorizationOIDC,
    91  		UserAuthorizationHandler: mockusertokens.NewMockVerifier(ctrl),
    92  	}
    93  }
    94  
    95  func newAPIAuthProcessor(ctrl *gomock.Controller) (*serviceregistry.Registry, *pucontext.PUContext, secrets.Secrets) {
    96  
    97  	contextID := "test"
    98  	baseService := newBaseApplicationServices(ctrl, "base", "10.1.1.0/24", uint16(80), uint16(443), uint16(80), false)
    99  	externalService := newBaseApplicationServices(ctrl, "external", "45.0.0.0/8", uint16(80), uint16(443), uint16(80), true)
   100  	externalBadService := newBaseApplicationServices(ctrl, "external", "100.0.0.0/8", uint16(80), uint16(443), uint16(80), true)
   101  
   102  	exposedServices := policy.ApplicationServicesList{baseService}
   103  	dependentServices := policy.ApplicationServicesList{baseService, externalService, externalBadService}
   104  
   105  	networkACLs := policy.IPRuleList{
   106  		{
   107  			Addresses: []string{"10.1.1.0/24"},
   108  			Ports:     []string{"80"},
   109  			Protocols: []string{"6"},
   110  			Policy: &policy.FlowPolicy{
   111  				Action:    policy.Accept,
   112  				PolicyID:  policyID,
   113  				ServiceID: serviceID,
   114  				Labels:    []string{"service=external"},
   115  			},
   116  		},
   117  		{
   118  			Addresses: []string{"45.0.0.0/8"},
   119  			Ports:     []string{"80"},
   120  			Protocols: []string{"6"},
   121  			Policy: &policy.FlowPolicy{
   122  				Action:    policy.Accept,
   123  				PolicyID:  policyID,
   124  				ServiceID: serviceID,
   125  				Labels:    []string{"service=external"},
   126  			},
   127  		},
   128  		{
   129  			Addresses: []string{"100.0.0.0/8"},
   130  			Ports:     []string{"80"},
   131  			Protocols: []string{"6"},
   132  			Policy: &policy.FlowPolicy{
   133  				Action:        policy.Reject,
   134  				PolicyID:      rejectPolicyID,
   135  				ServiceID:     rejectServiceID,
   136  				ObserveAction: policy.ObserveApply,
   137  				Labels:        []string{"service=external"},
   138  			},
   139  		},
   140  	}
   141  
   142  	applicationACLs := policy.IPRuleList{
   143  		{
   144  			Addresses: []string{"100.0.0.0/8"},
   145  			Ports:     []string{"80"},
   146  			Protocols: []string{"6"},
   147  			Policy: &policy.FlowPolicy{
   148  				Action:    policy.Reject,
   149  				PolicyID:  rejectPolicyID,
   150  				ServiceID: rejectServiceID,
   151  				Labels:    []string{"service=external"},
   152  			},
   153  		},
   154  	}
   155  
   156  	plc := policy.NewPUPolicy(
   157  		contextID,
   158  		namespace,
   159  		policy.Police,
   160  		applicationACLs,
   161  		networkACLs,
   162  		policy.DNSRuleList{},
   163  		policy.TagSelectorList{},
   164  		policy.TagSelectorList{
   165  			policy.TagSelector{
   166  				Clause: []policy.KeyValueOperator{
   167  					{
   168  						Key:      "app",
   169  						Value:    []string{"web"},
   170  						Operator: policy.Equal,
   171  						ID:       "somepolicy",
   172  					},
   173  				},
   174  				Policy: &policy.FlowPolicy{
   175  					Action:        policy.Accept,
   176  					ServiceID:     "pu" + serviceID,
   177  					PolicyID:      "pu" + policyID,
   178  					ObserveAction: policy.ObserveApply,
   179  				},
   180  			},
   181  			policy.TagSelector{
   182  				Clause: []policy.KeyValueOperator{
   183  					{
   184  						Key:      "app",
   185  						Value:    []string{"bad"},
   186  						Operator: policy.Equal,
   187  						ID:       "rejectpolicy",
   188  					},
   189  				},
   190  				Policy: &policy.FlowPolicy{
   191  					Action:   policy.Reject,
   192  					PolicyID: "reject" + policyID,
   193  				},
   194  			},
   195  		},
   196  		policy.NewTagStore(),
   197  		policy.NewTagStoreFromSlice([]string{appLabel, "type=aporeto"}),
   198  		nil,
   199  		nil,
   200  		0,
   201  		0,
   202  		exposedServices,
   203  		dependentServices,
   204  		[]string{appLabel},
   205  		policy.EnforcerMapping,
   206  		policy.Reject|policy.Log,
   207  		policy.Reject|policy.Log,
   208  	)
   209  
   210  	puInfo := policy.NewPUInfo(contextID, namespace, triremecommon.ContainerPU)
   211  	puInfo.Policy = plc
   212  	pctx, err := pucontext.NewPU(contextID, puInfo, nil, time.Second*1000)
   213  	So(err, ShouldBeNil)
   214  	_, s, _ := testhelper.NewTestCompactPKISecrets()
   215  
   216  	r := serviceregistry.Instance()
   217  	_, err = r.Register(contextID, puInfo, pctx, s)
   218  	So(err, ShouldBeNil)
   219  
   220  	return r, pctx, s
   221  }
   222  
   223  func Test_New(t *testing.T) {
   224  	Convey("When I create a new processor it should be correctly propulated", t, func() {
   225  		ctrl := gomock.NewController(t)
   226  		_, _, s := newAPIAuthProcessor(ctrl)
   227  		p := New("test", s)
   228  
   229  		So(p.puContext, ShouldEqual, "test")
   230  		So(p.secrets, ShouldEqual, s)
   231  	})
   232  }
   233  
   234  func Test_ApplicationRequest(t *testing.T) {
   235  	Convey("Given a valid authorization processor", t, func() {
   236  		ctrl := gomock.NewController(t)
   237  		_, pctx, s := newAPIAuthProcessor(ctrl)
   238  		p := New("test", s)
   239  
   240  		Convey("Given a request without context, it should error", func() {
   241  
   242  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   243  			r := &Request{
   244  				SourceAddress: &net.TCPAddr{
   245  					IP:   net.ParseIP("10.1.1.1"),
   246  					Port: 1000,
   247  				},
   248  				OriginalDestination: &net.TCPAddr{
   249  					IP:   net.ParseIP("20.1.1.1"),
   250  					Port: 8080,
   251  				},
   252  				Method:     "GET",
   253  				URL:        u,
   254  				RequestURI: "/public",
   255  				Header:     http.Header{},
   256  				Cookie:     nil,
   257  				TLS:        nil,
   258  			}
   259  			_, err := p.ApplicationRequest(r)
   260  			So(err, ShouldNotBeNil)
   261  
   262  			authErr, ok := err.(*AuthError)
   263  			So(ok, ShouldBeTrue)
   264  			So(authErr.Status(), ShouldEqual, http.StatusBadGateway)
   265  		})
   266  
   267  		Convey("Given a request with valid context that is not external, I should get a token", func() {
   268  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   269  			r := &Request{
   270  				SourceAddress: &net.TCPAddr{
   271  					IP:   net.ParseIP("10.1.1.1"),
   272  					Port: 1000,
   273  				},
   274  				OriginalDestination: &net.TCPAddr{
   275  					IP:   net.ParseIP("10.1.1.2"),
   276  					Port: 80,
   277  				},
   278  				Method:     "GET",
   279  				URL:        u,
   280  				RequestURI: "/public",
   281  				Header:     http.Header{},
   282  				Cookie:     nil,
   283  				TLS:        nil,
   284  			}
   285  			response, err := p.ApplicationRequest(r)
   286  			So(err, ShouldBeNil)
   287  			So(response, ShouldNotBeNil)
   288  			So(len(response.Token), ShouldBeGreaterThan, 0)
   289  			So(response.PUContext, ShouldEqual, pctx)
   290  			So(response.TLSListener, ShouldBeTrue)
   291  		})
   292  
   293  		Convey("Given a request for a public external service, I should accept it", func() {
   294  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   295  			r := &Request{
   296  				SourceAddress: &net.TCPAddr{
   297  					IP:   net.ParseIP("10.1.1.1"),
   298  					Port: 1000,
   299  				},
   300  				OriginalDestination: &net.TCPAddr{
   301  					IP:   net.ParseIP("45.1.1.1"),
   302  					Port: 80,
   303  				},
   304  				Method:     "GET",
   305  				URL:        u,
   306  				RequestURI: "/public",
   307  				Header:     http.Header{},
   308  				Cookie:     nil,
   309  				TLS:        nil,
   310  			}
   311  			response, err := p.ApplicationRequest(r)
   312  			So(err, ShouldBeNil)
   313  			So(response, ShouldNotBeNil)
   314  			So(len(response.Token), ShouldEqual, 0)
   315  			So(response.PUContext, ShouldEqual, pctx)
   316  			So(response.TLSListener, ShouldBeTrue)
   317  		})
   318  
   319  		Convey("Given a request for a controlled external service with valid policy, I should accept it", func() {
   320  			u, _ := url.Parse("http://www.foo.com/admin") // nolint
   321  			r := &Request{
   322  				SourceAddress: &net.TCPAddr{
   323  					IP:   net.ParseIP("10.1.1.1"),
   324  					Port: 1000,
   325  				},
   326  				OriginalDestination: &net.TCPAddr{
   327  					IP:   net.ParseIP("45.1.1.1"),
   328  					Port: 80,
   329  				},
   330  				Method:     "GET",
   331  				URL:        u,
   332  				RequestURI: "/admin",
   333  				Header:     http.Header{},
   334  				Cookie:     nil,
   335  				TLS:        nil,
   336  			}
   337  			response, err := p.ApplicationRequest(r)
   338  			So(err, ShouldBeNil)
   339  			So(response, ShouldNotBeNil)
   340  			So(len(response.Token), ShouldEqual, 0)
   341  			So(response.PUContext, ShouldEqual, pctx)
   342  			So(response.TLSListener, ShouldBeTrue)
   343  		})
   344  
   345  		Convey("Given a request for a controlled external service with forbidden policy, I should reject it", func() {
   346  			u, _ := url.Parse("http://www.foo.com/forbidden") // nolint
   347  			r := &Request{
   348  				SourceAddress: &net.TCPAddr{
   349  					IP:   net.ParseIP("10.1.1.1"),
   350  					Port: 1000,
   351  				},
   352  				OriginalDestination: &net.TCPAddr{
   353  					IP:   net.ParseIP("45.1.1.1"),
   354  					Port: 80,
   355  				},
   356  				Method:     "GET",
   357  				URL:        u,
   358  				RequestURI: "/forbidden",
   359  				Header:     http.Header{},
   360  				Cookie:     nil,
   361  				TLS:        nil,
   362  			}
   363  			_, err := p.ApplicationRequest(r)
   364  			So(err, ShouldNotBeNil)
   365  			authErr, ok := err.(*AuthError)
   366  			So(ok, ShouldBeTrue)
   367  			So(authErr.Status(), ShouldEqual, http.StatusForbidden)
   368  		})
   369  
   370  		Convey("Given a request for a controlled external service with an uknown URI, I should reject it", func() {
   371  			u, _ := url.Parse("http://www.foo.com/random") // nolint
   372  			r := &Request{
   373  				SourceAddress: &net.TCPAddr{
   374  					IP:   net.ParseIP("10.1.1.1"),
   375  					Port: 1000,
   376  				},
   377  				OriginalDestination: &net.TCPAddr{
   378  					IP:   net.ParseIP("45.1.1.1"),
   379  					Port: 80,
   380  				},
   381  				Method:     "GET",
   382  				URL:        u,
   383  				RequestURI: "/random",
   384  				Header:     http.Header{},
   385  				Cookie:     nil,
   386  				TLS:        nil,
   387  			}
   388  			_, err := p.ApplicationRequest(r)
   389  			So(err, ShouldNotBeNil)
   390  			authErr, ok := err.(*AuthError)
   391  			So(ok, ShouldBeTrue)
   392  			So(authErr.Status(), ShouldEqual, http.StatusForbidden)
   393  		})
   394  
   395  		Convey("Given a request for a an external service dropped by network rules it should be rejected", func() {
   396  			u, _ := url.Parse("http://www.foo.com/random") // nolint
   397  			r := &Request{
   398  				SourceAddress: &net.TCPAddr{
   399  					IP:   net.ParseIP("10.1.1.1"),
   400  					Port: 1000,
   401  				},
   402  				OriginalDestination: &net.TCPAddr{
   403  					IP:   net.ParseIP("100.1.1.1"),
   404  					Port: 80,
   405  				},
   406  				Method:     "GET",
   407  				URL:        u,
   408  				RequestURI: "/public",
   409  				Header:     http.Header{},
   410  				Cookie:     nil,
   411  				TLS:        nil,
   412  			}
   413  			_, err := p.ApplicationRequest(r)
   414  			So(err, ShouldNotBeNil)
   415  			authErr, ok := err.(*AuthError)
   416  			So(ok, ShouldBeTrue)
   417  			So(authErr.Status(), ShouldEqual, http.StatusNetworkAuthenticationRequired)
   418  		})
   419  	})
   420  }
   421  
   422  func Test_NetworkRequest(t *testing.T) {
   423  	Convey("Given a valid authorization processor", t, func() {
   424  		ctx, cancel := context.WithCancel(context.Background())
   425  		defer cancel()
   426  
   427  		ctrl := gomock.NewController(t)
   428  		_, pctx, s := newAPIAuthProcessor(ctrl)
   429  		p := New("test", s)
   430  
   431  		Convey("Requests for bad context should return errors", func() {
   432  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   433  			r := &Request{
   434  				SourceAddress: &net.TCPAddr{
   435  					IP:   net.ParseIP("10.1.1.1"),
   436  					Port: 1000,
   437  				},
   438  				OriginalDestination: &net.TCPAddr{
   439  					IP:   net.ParseIP("20.1.1.1"),
   440  					Port: 8080,
   441  				},
   442  				Method:     "GET",
   443  				URL:        u,
   444  				RequestURI: "/public",
   445  				Header:     http.Header{},
   446  				Cookie:     nil,
   447  				TLS:        nil,
   448  			}
   449  			_, err := p.NetworkRequest(ctx, r)
   450  			So(err, ShouldNotBeNil)
   451  
   452  			authErr, ok := err.(*AuthError)
   453  			So(ok, ShouldBeTrue)
   454  			So(authErr.Status(), ShouldEqual, http.StatusInternalServerError)
   455  		})
   456  
   457  		Convey("Requests a valid context with a drop network policy must be rejected", func() {
   458  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   459  			r := &Request{
   460  				SourceAddress: &net.TCPAddr{
   461  					IP:   net.ParseIP("100.1.1.1"),
   462  					Port: 1000,
   463  				},
   464  				OriginalDestination: &net.TCPAddr{
   465  					IP:   net.ParseIP("10.1.1.1"),
   466  					Port: 80,
   467  				},
   468  				Method:     "GET",
   469  				URL:        u,
   470  				RequestURI: "/public",
   471  				Header:     http.Header{},
   472  				Cookie:     nil,
   473  				TLS:        nil,
   474  			}
   475  			response, err := p.NetworkRequest(ctx, r)
   476  			So(err, ShouldNotBeNil)
   477  			So(response, ShouldNotBeNil)
   478  
   479  			authErr, ok := err.(*AuthError)
   480  			So(ok, ShouldBeTrue)
   481  			So(authErr.Status(), ShouldEqual, http.StatusNetworkAuthenticationRequired)
   482  			So(response.NetworkPolicyID, ShouldEqual, rejectPolicyID)
   483  			So(response.NetworkServiceID, ShouldEqual, rejectServiceID)
   484  			So(response.DropReason, ShouldEqual, collector.PolicyDrop)
   485  			So(response.SourceType, ShouldEqual, collector.EndPointTypeExternalIP)
   486  		})
   487  
   488  		Convey("Requests a valid context with an invalid token, I should get forbidden", func() {
   489  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   490  			h := http.Header{}
   491  			h.Add("X-APORETO-AUTH", "badvalue")
   492  			h.Add("X-APORETO-KEY", "badvalue")
   493  
   494  			r := &Request{
   495  				SourceAddress: &net.TCPAddr{
   496  					IP:   net.ParseIP("45.1.1.1"),
   497  					Port: 1000,
   498  				},
   499  				OriginalDestination: &net.TCPAddr{
   500  					IP:   net.ParseIP("10.1.1.1"),
   501  					Port: 80,
   502  				},
   503  				Method:     "GET",
   504  				URL:        u,
   505  				RequestURI: "/public",
   506  				Header:     h,
   507  				Cookie:     nil,
   508  				TLS:        nil,
   509  			}
   510  			response, err := p.NetworkRequest(ctx, r)
   511  			So(err, ShouldNotBeNil)
   512  			So(response, ShouldNotBeNil)
   513  
   514  			authErr, ok := err.(*AuthError)
   515  			So(ok, ShouldBeTrue)
   516  			So(authErr.Status(), ShouldEqual, http.StatusForbidden)
   517  			So(authErr.Message(), ShouldContainSubstring, "Invalid Authorization Token:")
   518  			So(response.NetworkPolicyID, ShouldEqual, policyID)
   519  			So(response.NetworkServiceID, ShouldEqual, serviceID)
   520  			So(response.DropReason, ShouldEqual, collector.PolicyDrop)
   521  			So(response.SourceType, ShouldEqual, collector.EndPointTypeExternalIP)
   522  		})
   523  
   524  		Convey("Requests a valid context with a valid Aporeto token to a public URL from a valid network it should succeed", func() {
   525  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   526  			token, err := servicetokens.CreateAndSign(
   527  				"somenode",
   528  				pctx.Identity().GetSlice(),
   529  				pctx.Scopes(),
   530  				pctx.ManagementID(),
   531  				DefaultValidity,
   532  				s.EncodingKey(),
   533  				nil,
   534  			)
   535  			So(err, ShouldBeNil)
   536  
   537  			h := http.Header{}
   538  			h.Add("X-APORETO-AUTH", token)
   539  			h.Add("X-APORETO-KEY", string(s.TransmittedKey()))
   540  
   541  			r := &Request{
   542  				SourceAddress: &net.TCPAddr{
   543  					IP:   net.ParseIP("45.1.1.1"),
   544  					Port: 1000,
   545  				},
   546  				OriginalDestination: &net.TCPAddr{
   547  					IP:   net.ParseIP("10.1.1.1"),
   548  					Port: 80,
   549  				},
   550  				Method:     "GET",
   551  				URL:        u,
   552  				RequestURI: "/public",
   553  				Header:     h,
   554  				Cookie:     nil,
   555  				TLS:        nil,
   556  			}
   557  			response, err := p.NetworkRequest(ctx, r)
   558  			So(err, ShouldBeNil)
   559  			So(response.NetworkPolicyID, ShouldEqual, policyID)
   560  			So(response.NetworkServiceID, ShouldEqual, serviceID)
   561  			So(response.SourceType, ShouldEqual, collector.EndPointTypePU)
   562  		})
   563  
   564  		Convey("Requests a valid context with a valid Aporeto token based on PU network policy it should succeed", func() {
   565  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   566  			token, err := servicetokens.CreateAndSign(
   567  				"somenode",
   568  				pctx.Identity().GetSlice(),
   569  				pctx.Scopes(),
   570  				pctx.ManagementID(),
   571  				DefaultValidity,
   572  				s.EncodingKey(),
   573  				nil,
   574  			)
   575  			So(err, ShouldBeNil)
   576  
   577  			h := http.Header{}
   578  			h.Add("X-APORETO-AUTH", token)
   579  			h.Add("X-APORETO-KEY", string(s.TransmittedKey()))
   580  
   581  			r := &Request{
   582  				SourceAddress: &net.TCPAddr{
   583  					IP:   net.ParseIP("60.1.1.1"),
   584  					Port: 1000,
   585  				},
   586  				OriginalDestination: &net.TCPAddr{
   587  					IP:   net.ParseIP("10.1.1.1"),
   588  					Port: 80,
   589  				},
   590  				Method:     "GET",
   591  				URL:        u,
   592  				RequestURI: "/public",
   593  				Header:     h,
   594  				Cookie:     nil,
   595  				TLS:        nil,
   596  			}
   597  			response, err := p.NetworkRequest(ctx, r)
   598  			So(err, ShouldBeNil)
   599  			So(response.ObservedPolicyID, ShouldEqual, "pu"+policyID)
   600  			So(response.ObservedAction, ShouldEqual, policy.Accept)
   601  			So(response.NetworkPolicyID, ShouldEqual, "pu"+policyID)
   602  			So(response.SourceType, ShouldEqual, collector.EndPointTypePU)
   603  		})
   604  
   605  		Convey("Requests a valid context with no Aporeto claims and no network policy, it should be dropped", func() {
   606  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   607  
   608  			r := &Request{
   609  				SourceAddress: &net.TCPAddr{
   610  					IP:   net.ParseIP("60.1.1.1"),
   611  					Port: 1000,
   612  				},
   613  				OriginalDestination: &net.TCPAddr{
   614  					IP:   net.ParseIP("10.1.1.1"),
   615  					Port: 80,
   616  				},
   617  				Method:     "GET",
   618  				URL:        u,
   619  				RequestURI: "/public",
   620  				Header:     http.Header{},
   621  				Cookie:     nil,
   622  				TLS:        nil,
   623  			}
   624  			response, err := p.NetworkRequest(ctx, r)
   625  			So(err, ShouldNotBeNil)
   626  			authErr, ok := err.(*AuthError)
   627  			So(ok, ShouldBeTrue)
   628  			So(authErr.Status(), ShouldEqual, http.StatusNetworkAuthenticationRequired)
   629  			So(response.NetworkPolicyID, ShouldEqual, collector.DefaultEndPoint)
   630  			So(response.SourceType, ShouldEqual, collector.EndPointTypeExternalIP)
   631  		})
   632  
   633  		Convey("Requests a valid context with a valid Aporeto token but network reject, it should be rejected", func() {
   634  			u, _ := url.Parse("http://www.foo.com/public") // nolint
   635  			badTags := append(pctx.Identity().GetSlice(), "app=bad")
   636  			token, err := servicetokens.CreateAndSign(
   637  				"badnode",
   638  				badTags,
   639  				pctx.Scopes(),
   640  				"badnodeID",
   641  				DefaultValidity,
   642  				s.EncodingKey(),
   643  				nil,
   644  			)
   645  			So(err, ShouldBeNil)
   646  
   647  			h := http.Header{}
   648  			h.Add("X-APORETO-AUTH", token)
   649  			h.Add("X-APORETO-KEY", string(s.TransmittedKey()))
   650  
   651  			r := &Request{
   652  				SourceAddress: &net.TCPAddr{
   653  					IP:   net.ParseIP("60.1.1.1"),
   654  					Port: 1000,
   655  				},
   656  				OriginalDestination: &net.TCPAddr{
   657  					IP:   net.ParseIP("10.1.1.1"),
   658  					Port: 80,
   659  				},
   660  				Method:     "GET",
   661  				URL:        u,
   662  				RequestURI: "/public",
   663  				Header:     h,
   664  				Cookie:     nil,
   665  				TLS:        nil,
   666  			}
   667  			response, err := p.NetworkRequest(ctx, r)
   668  			So(err, ShouldNotBeNil)
   669  			So(response.NetworkPolicyID, ShouldEqual, "reject"+policyID)
   670  			So(response.SourceType, ShouldEqual, collector.EndPointTypePU)
   671  		})
   672  
   673  		Convey("Requests a valid context with a valid Aporeto token to a private URL it should succeed", func() {
   674  			u, _ := url.Parse("http://www.foo.com/admin") // nolint
   675  			token, err := servicetokens.CreateAndSign(
   676  				"somenode",
   677  				pctx.Identity().GetSlice(),
   678  				pctx.Scopes(),
   679  				pctx.ManagementID(),
   680  				DefaultValidity,
   681  				s.EncodingKey(),
   682  				nil,
   683  			)
   684  			So(err, ShouldBeNil)
   685  
   686  			h := http.Header{}
   687  			h.Add("X-APORETO-AUTH", token)
   688  			h.Add("X-APORETO-KEY", string(s.TransmittedKey()))
   689  
   690  			r := &Request{
   691  				SourceAddress: &net.TCPAddr{
   692  					IP:   net.ParseIP("45.1.1.1"),
   693  					Port: 1000,
   694  				},
   695  				OriginalDestination: &net.TCPAddr{
   696  					IP:   net.ParseIP("10.1.1.1"),
   697  					Port: 80,
   698  				},
   699  				Method:     "GET",
   700  				URL:        u,
   701  				RequestURI: "/admin",
   702  				Header:     h,
   703  				Cookie:     nil,
   704  				TLS:        nil,
   705  			}
   706  			response, err := p.NetworkRequest(ctx, r)
   707  			So(err, ShouldBeNil)
   708  			So(response.NetworkPolicyID, ShouldEqual, policyID)
   709  			So(response.NetworkServiceID, ShouldEqual, serviceID)
   710  			So(response.SourceType, ShouldEqual, collector.EndPointTypePU)
   711  		})
   712  
   713  		Convey("Requests a valid context with a valid Aporeto token to a forbidden URL it should return error", func() {
   714  			u, _ := url.Parse("http://www.foo.com/forbidden") // nolint
   715  			token, err := servicetokens.CreateAndSign(
   716  				"somenode",
   717  				pctx.Identity().GetSlice(),
   718  				pctx.Scopes(),
   719  				"forbiddennode",
   720  				DefaultValidity,
   721  				s.EncodingKey(),
   722  				nil,
   723  			)
   724  			So(err, ShouldBeNil)
   725  
   726  			h := http.Header{}
   727  			h.Add("X-APORETO-AUTH", token)
   728  			h.Add("X-APORETO-KEY", string(s.TransmittedKey()))
   729  
   730  			r := &Request{
   731  				SourceAddress: &net.TCPAddr{
   732  					IP:   net.ParseIP("45.1.1.1"),
   733  					Port: 1000,
   734  				},
   735  				OriginalDestination: &net.TCPAddr{
   736  					IP:   net.ParseIP("10.1.1.1"),
   737  					Port: 80,
   738  				},
   739  				Method:     "GET",
   740  				URL:        u,
   741  				RequestURI: "/forbidden",
   742  				Header:     h,
   743  				Cookie:     nil,
   744  				TLS:        nil,
   745  			}
   746  			response, err := p.NetworkRequest(ctx, r)
   747  			So(err, ShouldNotBeNil)
   748  			authError, ok := err.(*AuthError)
   749  			So(ok, ShouldBeTrue)
   750  			So(authError.Status(), ShouldEqual, http.StatusUnauthorized)
   751  			So(response.NetworkPolicyID, ShouldEqual, policyID)
   752  			So(response.NetworkServiceID, ShouldEqual, serviceID)
   753  			So(response.SourceType, ShouldEqual, collector.EndPointTypePU)
   754  		})
   755  	})
   756  }
   757  
   758  func Test_UserCredentials(t *testing.T) {
   759  
   760  	Convey("Given a valid authorizer", t, func() {
   761  		ctx, cancel := context.WithCancel(context.Background())
   762  		defer cancel()
   763  
   764  		ctrl := gomock.NewController(t)
   765  		serviceRegistry, _, s := newAPIAuthProcessor(ctrl)
   766  		p := New("test", s)
   767  		So(p, ShouldNotBeNil)
   768  
   769  		portContext, err := serviceRegistry.RetrieveExposedServiceContext(net.ParseIP("10.1.1.1"), 80, "")
   770  		So(err, ShouldBeNil)
   771  		So(portContext, ShouldNotBeNil)
   772  
   773  		verifier, ok := portContext.Service.UserAuthorizationHandler.(*mockusertokens.MockVerifier)
   774  		So(ok, ShouldBeTrue)
   775  
   776  		Convey("When the request is not TLS, there is no user data", func() {
   777  			u, _ := url.Parse("http://www.foo.com/admin")
   778  			d := &NetworkAuthResponse{}
   779  			r := &Request{
   780  				SourceAddress: &net.TCPAddr{
   781  					IP:   net.ParseIP("45.1.1.1"),
   782  					Port: 1000,
   783  				},
   784  				OriginalDestination: &net.TCPAddr{
   785  					IP:   net.ParseIP("10.1.1.1"),
   786  					Port: 80,
   787  				},
   788  				Method:     "GET",
   789  				URL:        u,
   790  				RequestURI: "/admin",
   791  				Header:     http.Header{},
   792  				Cookie:     nil,
   793  				TLS:        nil,
   794  			}
   795  			userCredentials(ctx, portContext, r, d)
   796  			So(len(d.UserAttributes), ShouldEqual, 0)
   797  		})
   798  
   799  		Convey("When the request is TLS and a user is identified, the claims are correct", func() {
   800  			u, _ := url.Parse("http://www.foo.com/admin")
   801  			d := &NetworkAuthResponse{}
   802  
   803  			r := &Request{
   804  				SourceAddress: &net.TCPAddr{
   805  					IP:   net.ParseIP("45.1.1.1"),
   806  					Port: 1000,
   807  				},
   808  				OriginalDestination: &net.TCPAddr{
   809  					IP:   net.ParseIP("10.1.1.1"),
   810  					Port: 80,
   811  				},
   812  				Method:     "GET",
   813  				URL:        u,
   814  				RequestURI: "/admin",
   815  				Header:     http.Header{},
   816  				Cookie:     nil,
   817  				TLS:        &tls.ConnectionState{},
   818  			}
   819  			verifier.EXPECT().Validate(ctx, gomock.Any()).Return([]string{"user=flash"}, false, "", nil)
   820  			userCredentials(ctx, portContext, r, d)
   821  			So(len(d.UserAttributes), ShouldEqual, 1)
   822  			So(d.UserAttributes[0], ShouldEqual, "user=flash")
   823  			So(d.SourceType, ShouldEqual, collector.EndPointTypeClaims)
   824  			So(d.Redirect, ShouldBeFalse)
   825  		})
   826  
   827  		Convey("When the request is TLS and user authorization fails with a redirect, the redirect should be set", func() {
   828  			u, _ := url.Parse("http://www.foo.com/admin")
   829  			d := &NetworkAuthResponse{}
   830  
   831  			h := http.Header{}
   832  			h.Add("Authorization", "Bearer MockJWTToken")
   833  			r := &Request{
   834  				SourceAddress: &net.TCPAddr{
   835  					IP:   net.ParseIP("45.1.1.1"),
   836  					Port: 1000,
   837  				},
   838  				OriginalDestination: &net.TCPAddr{
   839  					IP:   net.ParseIP("10.1.1.1"),
   840  					Port: 80,
   841  				},
   842  				Method:     "GET",
   843  				URL:        u,
   844  				RequestURI: "/admin",
   845  				Header:     http.Header{},
   846  				Cookie:     nil,
   847  				TLS:        &tls.ConnectionState{},
   848  			}
   849  			verifier.EXPECT().Validate(ctx, gomock.Any()).Return(nil, true, "MockJWTToken", fmt.Errorf("auth failed"))
   850  			userCredentials(ctx, portContext, r, d)
   851  			So(len(d.UserAttributes), ShouldEqual, 0)
   852  			So(d.Redirect, ShouldBeTrue)
   853  		})
   854  
   855  		Convey("When the request is TLS and user authorization succeeds with a refresh token, the cookie must be set", func() {
   856  			u, _ := url.Parse("http://www.foo.com/admin")
   857  			d := &NetworkAuthResponse{}
   858  
   859  			h := http.Header{}
   860  			h.Add("Authorization", "Bearer MockJWTToken")
   861  			r := &Request{
   862  				SourceAddress: &net.TCPAddr{
   863  					IP:   net.ParseIP("45.1.1.1"),
   864  					Port: 1000,
   865  				},
   866  				OriginalDestination: &net.TCPAddr{
   867  					IP:   net.ParseIP("10.1.1.1"),
   868  					Port: 80,
   869  				},
   870  				Method:     "GET",
   871  				URL:        u,
   872  				RequestURI: "/admin",
   873  				Header:     http.Header{},
   874  				Cookie:     nil,
   875  				TLS:        &tls.ConnectionState{},
   876  			}
   877  			verifier.EXPECT().Validate(ctx, gomock.Any()).Return(nil, true, "NewToken", fmt.Errorf("auth failed"))
   878  			userCredentials(ctx, portContext, r, d)
   879  			So(len(d.UserAttributes), ShouldEqual, 0)
   880  			So(d.Redirect, ShouldBeTrue)
   881  			So(d.Cookie, ShouldNotBeNil)
   882  			So(d.Cookie.Name, ShouldEqual, "X-APORETO-AUTH")
   883  			So(d.Cookie.Value, ShouldEqual, "NewToken")
   884  			So(d.Cookie.HttpOnly, ShouldBeTrue)
   885  			So(d.Cookie.Secure, ShouldBeTrue)
   886  			So(d.Cookie.Path, ShouldEqual, "/")
   887  		})
   888  	})
   889  }