mirror of https://github.com/grafana/grafana.git
				
				
				
			ext_jwt: switch to new authlib (#87157)
This commit is contained in:
		
							parent
							
								
									babfa2beac
								
							
						
					
					
						commit
						0c59baf62d
					
				
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -87,7 +87,7 @@ require ( | ||||||
| 	github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group | 	github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group | ||||||
| 	github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad | 	github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad | ||||||
| 	github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 // @grafana/alerting-squad-backend | 	github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 // @grafana/alerting-squad-backend | ||||||
| 	github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 // @grafana/identity-access-team | 	github.com/grafana/authlib v0.0.0-20240503035720-d1f918d6254a // @grafana/identity-access-team | ||||||
| 	github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad | 	github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad | ||||||
| 	github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code | 	github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code | ||||||
| 	github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics | 	github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -2150,8 +2150,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm | ||||||
| github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
| github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 h1:v4aQ0cde8SCzNRrD2RczzmFolEkXWriSY9tKakAD0ng= | github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 h1:v4aQ0cde8SCzNRrD2RczzmFolEkXWriSY9tKakAD0ng= | ||||||
| github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36/go.mod h1:8nOsn7PWmttOmWiR7bvYIl3VLl+tIq72ZF+1y54w36M= | github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36/go.mod h1:8nOsn7PWmttOmWiR7bvYIl3VLl+tIq72ZF+1y54w36M= | ||||||
| github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 h1:lGEuhD/KhhN1OiPrvwQejl9Lg8MvaHdj3lHZNref4is= | github.com/grafana/authlib v0.0.0-20240503035720-d1f918d6254a h1:9I4HHhjS634CWcsCS82wGvpkveypCzMe+2DMuzKoW5A= | ||||||
| github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4= | github.com/grafana/authlib v0.0.0-20240503035720-d1f918d6254a/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4= | ||||||
| github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= | github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= | ||||||
| github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= | github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= | ||||||
| github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= | github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								go.work.sum
								
								
								
								
							
							
						
						
									
										41
									
								
								go.work.sum
								
								
								
								
							|  | @ -1,4 +1,4 @@ | ||||||
| buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= | buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230721003620-2341cbb21958.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= | ||||||
| buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= | buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= | ||||||
| buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1 h1:vp9EaPFSb75qe/793x58yE5fY1IJ/gdxb/kcDUzavtI= | buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1 h1:vp9EaPFSb75qe/793x58yE5fY1IJ/gdxb/kcDUzavtI= | ||||||
| buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1/go.mod h1:YDq2B5X5BChU0lxAG5MxHpDb8mx1fv9OGtF2mwOe7hY= | buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1/go.mod h1:YDq2B5X5BChU0lxAG5MxHpDb8mx1fv9OGtF2mwOe7hY= | ||||||
|  | @ -512,19 +512,15 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ | ||||||
| github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= | github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= | ||||||
| github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= | ||||||
| github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= | github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= | ||||||
| github.com/bufbuild/protovalidate-go v0.2.1 h1:pJr07sYhliyfj/STAM7hU4J3FKpVeLVKvOBmOTN8j+s= |  | ||||||
| github.com/bufbuild/protovalidate-go v0.2.1/go.mod h1:e7XXDtlxj5vlEyAgsrxpzayp4cEMKCSSb8ZCkin+MVA= | github.com/bufbuild/protovalidate-go v0.2.1/go.mod h1:e7XXDtlxj5vlEyAgsrxpzayp4cEMKCSSb8ZCkin+MVA= | ||||||
| github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= | github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= | ||||||
| github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= |  | ||||||
| github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= | github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= | ||||||
| github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= | github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= | ||||||
| github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= | ||||||
| github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= | github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= | ||||||
| github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= | ||||||
| github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= | ||||||
| github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= |  | ||||||
| github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= | ||||||
| github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= |  | ||||||
| github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= | ||||||
| github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= | github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= | ||||||
| github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= | github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= | ||||||
|  | @ -722,8 +718,7 @@ github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b h1:Ha+kSIoTutf4ytlVw | ||||||
| github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b/go.mod h1:3UsooRp7yW5/NJQBlXcTsAHOoykEhNUYXkQ3r6ehEEY= | github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b/go.mod h1:3UsooRp7yW5/NJQBlXcTsAHOoykEhNUYXkQ3r6ehEEY= | ||||||
| github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= | github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= | ||||||
| github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= | github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= | ||||||
| github.com/grafana/grafana-aws-sdk v0.25.1 h1:waJERJueqB1GldclAh5+tBcY9Ju9AOO9xYSJEnOAuf4= | github.com/grafana/grafana-azure-sdk-go/v2 v2.0.2 h1:CWT7mOBPUht9n7F/NiBQnEM05pFmCP3Z8CZPGCVC1tM= | ||||||
| github.com/grafana/grafana-aws-sdk v0.25.1/go.mod h1:3zghFF6edrxn0d6k6X9HpGZXDH+VfA+MwD2Pc/9X0ec= |  | ||||||
| github.com/grafana/grafana-azure-sdk-go/v2 v2.0.2/go.mod h1:s8GLONgVh/svnSsO0Eo+OgXc/RZqozI5/0n+pNm3MEE= | github.com/grafana/grafana-azure-sdk-go/v2 v2.0.2/go.mod h1:s8GLONgVh/svnSsO0Eo+OgXc/RZqozI5/0n+pNm3MEE= | ||||||
| github.com/grafana/grafana-plugin-sdk-go v0.212.0/go.mod h1:qsI4ktDf0lig74u8SLPJf9zRdVxWV/W4Wi+Ox6gifgs= | github.com/grafana/grafana-plugin-sdk-go v0.212.0/go.mod h1:qsI4ktDf0lig74u8SLPJf9zRdVxWV/W4Wi+Ox6gifgs= | ||||||
| github.com/grafana/grafana-plugin-sdk-go v0.215.0/go.mod h1:nBsh3jRItKQUXDF2BQkiQCPxqrsSQeb+7hiFyJTO1RE= | github.com/grafana/grafana-plugin-sdk-go v0.215.0/go.mod h1:nBsh3jRItKQUXDF2BQkiQCPxqrsSQeb+7hiFyJTO1RE= | ||||||
|  | @ -732,22 +727,17 @@ github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240426134450-5fe9f7b9dfd4 | ||||||
| github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240426134450-5fe9f7b9dfd4/go.mod h1:UBDIuvdUGUI5fMDHDAl6yAVpFhfwl5ojMaw1N68775w= | github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240426134450-5fe9f7b9dfd4/go.mod h1:UBDIuvdUGUI5fMDHDAl6yAVpFhfwl5ojMaw1N68775w= | ||||||
| github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8 h1:pyWJN79uW8QHZiQRasHGLCEkXSr3k6HCjdr0J2jZ3rU= | github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8 h1:pyWJN79uW8QHZiQRasHGLCEkXSr3k6HCjdr0J2jZ3rU= | ||||||
| github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8/go.mod h1:u4K9vVN6eU86loO68977eTXGypC4brUCnk4sfDzutZU= | github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8/go.mod h1:u4K9vVN6eU86loO68977eTXGypC4brUCnk4sfDzutZU= | ||||||
|  | github.com/grafana/grafana-plugin-sdk-go v0.228.0 h1:LlPqyB+RZTtDy8RVYD7iQVJW5A0gMoGSI/+Ykz8HebQ= | ||||||
|  | github.com/grafana/grafana-plugin-sdk-go v0.228.0/go.mod h1:u4K9vVN6eU86loO68977eTXGypC4brUCnk4sfDzutZU= | ||||||
| github.com/grafana/grafana/pkg/promlib v0.0.3/go.mod h1:3El4NlsfALz8QQCbEGHGFvJUG+538QLMuALRhZ3pcoo= | github.com/grafana/grafana/pkg/promlib v0.0.3/go.mod h1:3El4NlsfALz8QQCbEGHGFvJUG+538QLMuALRhZ3pcoo= | ||||||
| github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= | ||||||
| github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= | github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= | ||||||
| github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= | github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= | ||||||
| github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= | github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= | ||||||
| github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= |  | ||||||
| github.com/hamba/avro/v2 v2.17.2 h1:6PKpEWzJfNnvBgn7m2/8WYaDOUASxfDU+Jyb4ojDgFY= |  | ||||||
| github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= | github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= | ||||||
| github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= |  | ||||||
| github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek= |  | ||||||
| github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU= |  | ||||||
| github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= | github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= | ||||||
| github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= | github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= | ||||||
| github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= | github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= | ||||||
| github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= |  | ||||||
| github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= |  | ||||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | ||||||
| github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= | ||||||
| github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= | github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= | ||||||
|  | @ -801,11 +791,6 @@ github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA | ||||||
| github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= | github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= | ||||||
| github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o= | github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o= | ||||||
| github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0= | github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0= | ||||||
| github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo= |  | ||||||
| github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= |  | ||||||
| github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= |  | ||||||
| github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= |  | ||||||
| github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY= |  | ||||||
| github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= | github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= | ||||||
| github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= | github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= | ||||||
| github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= | github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= | ||||||
|  | @ -820,13 +805,9 @@ github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY | ||||||
| github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= | github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= | ||||||
| github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= | github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= | ||||||
| github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= | github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= | ||||||
| github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= |  | ||||||
| github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= |  | ||||||
| github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= |  | ||||||
| github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||||
| github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= | github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= | ||||||
| github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= | ||||||
| github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= |  | ||||||
| github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= | github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= | ||||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= | ||||||
| github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= | ||||||
|  | @ -1004,10 +985,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I | ||||||
| github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= | github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= | ||||||
| github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= | ||||||
| github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | ||||||
| github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= |  | ||||||
| github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= | github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= | ||||||
| github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= |  | ||||||
| github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= |  | ||||||
| github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= | github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= | ||||||
| github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= | github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= | ||||||
| github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= | ||||||
|  | @ -1021,13 +999,9 @@ github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RU | ||||||
| github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= | github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= | ||||||
| github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA= | github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA= | ||||||
| github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= | github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= | ||||||
| github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo= |  | ||||||
| github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | ||||||
| github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= |  | ||||||
| github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= | ||||||
| github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= |  | ||||||
| github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | ||||||
| github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= |  | ||||||
| github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= | github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= | ||||||
| github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= | github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= | ||||||
| github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= | github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= | ||||||
|  | @ -1037,7 +1011,6 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm | ||||||
| github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= | github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= | ||||||
| github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= | ||||||
| github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= | ||||||
| github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= |  | ||||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||||
| github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= | github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= | ||||||
| github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= | github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= | ||||||
|  | @ -1149,7 +1122,6 @@ go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= | ||||||
| go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||||
| go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= | ||||||
| go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= | go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= | ||||||
| golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= |  | ||||||
| golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= | golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= | ||||||
| golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= | ||||||
|  | @ -1243,14 +1215,9 @@ gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI | ||||||
| gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | ||||||
| gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= | gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= | ||||||
| gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= | gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= | ||||||
| gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= |  | ||||||
| gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= |  | ||||||
| honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= |  | ||||||
| k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6 h1:WN8Lymy+dCTDHgn4vhUSNIB6U+0sDiv/c9Zdr0UeAnI= | k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6 h1:WN8Lymy+dCTDHgn4vhUSNIB6U+0sDiv/c9Zdr0UeAnI= | ||||||
| k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6/go.mod h1:l0ukbPS0lwFxOzSq5ZqjutzF+5IL2TLp495PswRPSZk= | k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6/go.mod h1:l0ukbPS0lwFxOzSq5ZqjutzF+5IL2TLp495PswRPSZk= | ||||||
| k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= |  | ||||||
| k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= | k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= | ||||||
| k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= |  | ||||||
| k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= | k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= | ||||||
| k8s.io/kms v0.29.0/go.mod h1:mB0f9HLxRXeXUfHfn1A7rpwOlzXI1gIWu86z6buNoYA= | k8s.io/kms v0.29.0/go.mod h1:mB0f9HLxRXeXUfHfn1A7rpwOlzXI1gIWu86z6buNoYA= | ||||||
| k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= | ||||||
|  |  | ||||||
|  | @ -4,15 +4,13 @@ import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"slices" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-jose/go-jose/v3/jwt" | 	"github.com/go-jose/go-jose/v3/jwt" | ||||||
| 
 |  | ||||||
| 	authlib "github.com/grafana/authlib/authn" | 	authlib "github.com/grafana/authlib/authn" | ||||||
| 
 |  | ||||||
| 	"github.com/grafana/grafana/pkg/infra/log" | 	"github.com/grafana/grafana/pkg/infra/log" | ||||||
|  | 	"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" | ||||||
| 	"github.com/grafana/grafana/pkg/services/authn" | 	"github.com/grafana/grafana/pkg/services/authn" | ||||||
| 	"github.com/grafana/grafana/pkg/services/login" | 	"github.com/grafana/grafana/pkg/services/login" | ||||||
| 	"github.com/grafana/grafana/pkg/services/signingkeys" | 	"github.com/grafana/grafana/pkg/services/signingkeys" | ||||||
|  | @ -22,10 +20,6 @@ import ( | ||||||
| 
 | 
 | ||||||
| var _ authn.Client = new(ExtendedJWT) | var _ authn.Client = new(ExtendedJWT) | ||||||
| 
 | 
 | ||||||
| var ( |  | ||||||
| 	acceptedSigningMethods = []string{"RS256", "ES256"} |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const ( | const ( | ||||||
| 	rfc9068ShortMediaType          = "at+jwt" | 	rfc9068ShortMediaType          = "at+jwt" | ||||||
| 	extJWTAuthenticationHeaderName = "X-Access-Token" | 	extJWTAuthenticationHeaderName = "X-Access-Token" | ||||||
|  | @ -34,7 +28,14 @@ const ( | ||||||
| 
 | 
 | ||||||
| func ProvideExtendedJWT(userService user.Service, cfg *setting.Cfg, | func ProvideExtendedJWT(userService user.Service, cfg *setting.Cfg, | ||||||
| 	signingKeys signingkeys.Service) *ExtendedJWT { | 	signingKeys signingkeys.Service) *ExtendedJWT { | ||||||
| 	verifier := authlib.NewVerifier[ExtendedJWTClaims](authlib.IDVerifierConfig{ | 	verifier := authlib.NewAccessTokenVerifier(authlib.VerifierConfig{ | ||||||
|  | 		SigningKeysURL: cfg.ExtJWTAuth.JWKSUrl, | ||||||
|  | 		AllowedAudiences: []string{ | ||||||
|  | 			cfg.ExtJWTAuth.ExpectAudience, | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	idTokenVerifier := authlib.NewIDTokenVerifier(authlib.VerifierConfig{ | ||||||
| 		SigningKeysURL: cfg.ExtJWTAuth.JWKSUrl, | 		SigningKeysURL: cfg.ExtJWTAuth.JWKSUrl, | ||||||
| 		AllowedAudiences: []string{ | 		AllowedAudiences: []string{ | ||||||
| 			cfg.ExtJWTAuth.ExpectAudience, | 			cfg.ExtJWTAuth.ExpectAudience, | ||||||
|  | @ -42,44 +43,39 @@ func ProvideExtendedJWT(userService user.Service, cfg *setting.Cfg, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	return &ExtendedJWT{ | 	return &ExtendedJWT{ | ||||||
| 		cfg:         cfg, | 		cfg:                 cfg, | ||||||
| 		log:         log.New(authn.ClientExtendedJWT), | 		log:                 log.New(authn.ClientExtendedJWT), | ||||||
| 		userService: userService, | 		userService:         userService, | ||||||
| 		signingKeys: signingKeys, | 		signingKeys:         signingKeys, | ||||||
| 		verifier:    verifier, | 		accessTokenVerifier: verifier, | ||||||
|  | 		namespaceMapper:     request.GetNamespaceMapper(cfg), | ||||||
|  | 
 | ||||||
|  | 		idTokenVerifier: idTokenVerifier, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ExtendedJWT struct { | type ExtendedJWT struct { | ||||||
| 	cfg         *setting.Cfg | 	cfg                 *setting.Cfg | ||||||
| 	log         log.Logger | 	log                 log.Logger | ||||||
| 	userService user.Service | 	userService         user.Service | ||||||
| 	signingKeys signingkeys.Service | 	signingKeys         signingkeys.Service | ||||||
| 	verifier    authlib.Verifier[ExtendedJWTClaims] | 	accessTokenVerifier authlib.Verifier[authlib.AccessTokenClaims] | ||||||
| } | 	idTokenVerifier     authlib.Verifier[authlib.IDTokenClaims] | ||||||
| 
 | 	namespaceMapper     request.NamespaceMapper | ||||||
| type ExtendedJWTClaims struct { |  | ||||||
| 	jwt.Claims |  | ||||||
| 	// Access policy scopes
 |  | ||||||
| 	Scopes []string `json:"scopes"` |  | ||||||
| 	// Grafana roles
 |  | ||||||
| 	Permissions []string `json:"permissions"` |  | ||||||
| 	// On-behalf-of user
 |  | ||||||
| 	DelegatedPermissions []string `json:"delegatedPermissions"` |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ExtendedJWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) { | func (s *ExtendedJWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) { | ||||||
| 	jwtToken := s.retrieveAuthenticationToken(r.HTTPRequest) | 	jwtToken := s.retrieveAuthenticationToken(r.HTTPRequest) | ||||||
| 
 | 
 | ||||||
| 	claims, err := s.verifyRFC9068Token(ctx, jwtToken, rfc9068ShortMediaType) | 	claims, err := s.accessTokenVerifier.Verify(ctx, jwtToken) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.log.Error("Failed to verify JWT", "error", err) | 		s.log.Error("Failed to verify access token", "error", err) | ||||||
| 		return nil, errJWTInvalid.Errorf("Failed to verify JWT: %w", err) | 		return nil, errJWTInvalid.Errorf("Failed to verify access token: %w", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	idToken := s.retrieveAuthorizationToken(r.HTTPRequest) | 	idToken := s.retrieveAuthorizationToken(r.HTTPRequest) | ||||||
| 	if idToken != "" { | 	if idToken != "" { | ||||||
| 		idTokenClaims, err := s.verifyRFC9068Token(ctx, idToken, "jwt") | 		idTokenClaims, err := s.idTokenVerifier.Verify(ctx, idToken) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			s.log.Error("Failed to verify id token", "error", err) | 			s.log.Error("Failed to verify id token", "error", err) | ||||||
| 			return nil, errJWTInvalid.Errorf("Failed to verify id token: %w", err) | 			return nil, errJWTInvalid.Errorf("Failed to verify id token: %w", err) | ||||||
|  | @ -95,8 +91,18 @@ func (s *ExtendedJWT) IsEnabled() bool { | ||||||
| 	return s.cfg.ExtJWTAuth.Enabled | 	return s.cfg.ExtJWTAuth.Enabled | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ExtendedJWT) authenticateAsUser(idTokenClaims, | func (s *ExtendedJWT) authenticateAsUser(idTokenClaims *authlib.Claims[authlib.IDTokenClaims], | ||||||
| 	accessTokenClaims *ExtendedJWTClaims) (*authn.Identity, error) { | 	accessTokenClaims *authlib.Claims[authlib.AccessTokenClaims]) (*authn.Identity, error) { | ||||||
|  | 	// compare the incoming namespace claim against what namespaceMapper returns
 | ||||||
|  | 	if allowedNamespace := s.namespaceMapper(s.getDefaultOrgID()); idTokenClaims.Rest.Namespace != allowedNamespace { | ||||||
|  | 		return nil, errJWTDisallowedNamespaceClaim | ||||||
|  | 	} | ||||||
|  | 	// since id token claims can never have a wildcard ("*") namespace claim, the below comparison effectively
 | ||||||
|  | 	// disallows wildcard claims in access tokens here in Grafana (they are only meant for service layer)
 | ||||||
|  | 	if accessTokenClaims.Rest.Namespace != idTokenClaims.Rest.Namespace { | ||||||
|  | 		return nil, errJWTMismatchedNamespaceClaims.Errorf("id token namespace: %s, access token namespace: %s", idTokenClaims.Rest.Namespace, accessTokenClaims.Rest.Namespace) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Only allow access policies to impersonate
 | 	// Only allow access policies to impersonate
 | ||||||
| 	if !strings.HasPrefix(accessTokenClaims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) { | 	if !strings.HasPrefix(accessTokenClaims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) { | ||||||
| 		s.log.Error("Invalid subject", "subject", accessTokenClaims.Subject) | 		s.log.Error("Invalid subject", "subject", accessTokenClaims.Subject) | ||||||
|  | @ -122,18 +128,23 @@ func (s *ExtendedJWT) authenticateAsUser(idTokenClaims, | ||||||
| 		ClientParams: authn.ClientParams{ | 		ClientParams: authn.ClientParams{ | ||||||
| 			SyncPermissions: true, | 			SyncPermissions: true, | ||||||
| 			FetchPermissionsParams: authn.FetchPermissionsParams{ | 			FetchPermissionsParams: authn.FetchPermissionsParams{ | ||||||
| 				ActionsLookup: accessTokenClaims.DelegatedPermissions, | 				ActionsLookup: accessTokenClaims.Rest.DelegatedPermissions, | ||||||
| 			}, | 			}, | ||||||
| 			FetchSyncedUser: true, | 			FetchSyncedUser: true, | ||||||
| 		}}, nil | 		}}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *ExtendedJWT) authenticateAsService(claims *ExtendedJWTClaims) (*authn.Identity, error) { | func (s *ExtendedJWT) authenticateAsService(claims *authlib.Claims[authlib.AccessTokenClaims]) (*authn.Identity, error) { | ||||||
| 	if !strings.HasPrefix(claims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) { | 	if !strings.HasPrefix(claims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) { | ||||||
| 		s.log.Error("Invalid subject", "subject", claims.Subject) | 		s.log.Error("Invalid subject", "subject", claims.Subject) | ||||||
| 		return nil, errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format") | 		return nil, errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// same as asUser, disallows wildcard claims in access tokens here in Grafana (they are only meant for service layer)
 | ||||||
|  | 	if allowedNamespace := s.namespaceMapper(s.getDefaultOrgID()); claims.Rest.Namespace != allowedNamespace { | ||||||
|  | 		return nil, errJWTDisallowedNamespaceClaim | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	id, err := authn.ParseNamespaceID(claims.Subject) | 	id, err := authn.ParseNamespaceID(claims.Subject) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | @ -147,7 +158,7 @@ func (s *ExtendedJWT) authenticateAsService(claims *ExtendedJWTClaims) (*authn.I | ||||||
| 		ClientParams: authn.ClientParams{ | 		ClientParams: authn.ClientParams{ | ||||||
| 			SyncPermissions: true, | 			SyncPermissions: true, | ||||||
| 			FetchPermissionsParams: authn.FetchPermissionsParams{ | 			FetchPermissionsParams: authn.FetchPermissionsParams{ | ||||||
| 				Roles: claims.Permissions, | 				Roles: claims.Rest.Permissions, | ||||||
| 			}, | 			}, | ||||||
| 			FetchSyncedUser: false, | 			FetchSyncedUser: false, | ||||||
| 		}, | 		}, | ||||||
|  | @ -202,59 +213,6 @@ func (s *ExtendedJWT) retrieveAuthorizationToken(httpRequest *http.Request) stri | ||||||
| 	return strings.TrimPrefix(jwtToken, "Bearer ") | 	return strings.TrimPrefix(jwtToken, "Bearer ") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // verifyRFC9068Token verifies the token against the RFC 9068 specification.
 |  | ||||||
| func (s *ExtendedJWT) verifyRFC9068Token(ctx context.Context, rawToken string, typ string) (*ExtendedJWTClaims, error) { |  | ||||||
| 	parsedToken, err := jwt.ParseSigned(rawToken) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("failed to parse JWT: %w", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(parsedToken.Headers) != 1 { |  | ||||||
| 		return nil, fmt.Errorf("only one header supported, got %d", len(parsedToken.Headers)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	parsedHeader := parsedToken.Headers[0] |  | ||||||
| 
 |  | ||||||
| 	typeHeader := parsedHeader.ExtraHeaders["typ"] |  | ||||||
| 	if typeHeader == nil { |  | ||||||
| 		return nil, fmt.Errorf("missing 'typ' field from the header") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jwtType := strings.ToLower(typeHeader.(string)) |  | ||||||
| 	if !strings.EqualFold(jwtType, typ) { |  | ||||||
| 		return nil, fmt.Errorf("invalid JWT type: %s", jwtType) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !slices.Contains(acceptedSigningMethods, parsedHeader.Algorithm) { |  | ||||||
| 		return nil, fmt.Errorf("invalid algorithm: %s. Accepted algorithms: %s", |  | ||||||
| 			parsedHeader.Algorithm, strings.Join(acceptedSigningMethods, ", ")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	keyID := parsedHeader.KeyID |  | ||||||
| 	if keyID == "" { |  | ||||||
| 		return nil, fmt.Errorf("missing 'kid' field from the header") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	claims, err := s.verifier.Verify(ctx, rawToken) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("failed to verify JWT: %w", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if claims.Expiry == nil { |  | ||||||
| 		return nil, fmt.Errorf("missing 'exp' claim") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if claims.Subject == "" { |  | ||||||
| 		return nil, fmt.Errorf("missing 'sub' claim") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if claims.IssuedAt == nil { |  | ||||||
| 		return nil, fmt.Errorf("missing 'iat' claim") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return &claims.Rest, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (s *ExtendedJWT) getDefaultOrgID() int64 { | func (s *ExtendedJWT) getDefaultOrgID() int64 { | ||||||
| 	orgID := int64(1) | 	orgID := int64(1) | ||||||
| 	if s.cfg.AutoAssignOrg && s.cfg.AutoAssignOrgId > 0 { | 	if s.cfg.AutoAssignOrg && s.cfg.AutoAssignOrgId > 0 { | ||||||
|  |  | ||||||
|  | @ -28,9 +28,14 @@ import ( | ||||||
| 	"github.com/grafana/grafana/pkg/setting" | 	"github.com/grafana/grafana/pkg/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type ( | ||||||
|  | 	JWTAccessTokenClaims = authlib.Claims[authlib.AccessTokenClaims] | ||||||
|  | 	JWTIDTokenClaims     = authlib.Claims[authlib.IDTokenClaims] | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| var ( | var ( | ||||||
| 	validPayload = ExtendedJWTClaims{ | 	validPayload = JWTAccessTokenClaims{ | ||||||
| 		Claims: jwt.Claims{ | 		Claims: &jwt.Claims{ | ||||||
| 			Issuer:   "http://localhost:3000", | 			Issuer:   "http://localhost:3000", | ||||||
| 			Subject:  "access-policy:this-uid", | 			Subject:  "access-policy:this-uid", | ||||||
| 			Audience: jwt.Audience{"http://localhost:3000"}, | 			Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | @ -38,12 +43,15 @@ var ( | ||||||
| 			Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 			Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 			IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 			IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 		}, | 		}, | ||||||
| 		Scopes:               []string{"profile", "groups"}, | 		Rest: authlib.AccessTokenClaims{ | ||||||
| 		DelegatedPermissions: []string{"dashboards:create", "folders:read", "datasources:explore", "datasources.insights:read"}, | 			Scopes:               []string{"profile", "groups"}, | ||||||
| 		Permissions:          []string{"fixed:folders:reader"}, | 			DelegatedPermissions: []string{"dashboards:create", "folders:read", "datasources:explore", "datasources.insights:read"}, | ||||||
|  | 			Permissions:          []string{"fixed:folders:reader"}, | ||||||
|  | 			Namespace:            "default", // org ID of 1 is special and translates to default
 | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 	validIDPayload = ExtendedJWTClaims{ | 	validIDPayload = JWTIDTokenClaims{ | ||||||
| 		Claims: jwt.Claims{ | 		Claims: &jwt.Claims{ | ||||||
| 			Issuer:   "http://localhost:3000", | 			Issuer:   "http://localhost:3000", | ||||||
| 			Subject:  "user:2", | 			Subject:  "user:2", | ||||||
| 			Audience: jwt.Audience{"http://localhost:3000"}, | 			Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | @ -51,24 +59,61 @@ var ( | ||||||
| 			Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 			Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 			IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 			IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 		}, | 		}, | ||||||
| 		Scopes: []string{"profile", "groups"}, | 		Rest: authlib.IDTokenClaims{ | ||||||
|  | 			AuthenticatedBy: "extended_jwt", | ||||||
|  | 			Namespace:       "default", // org ID of 1 is special and translates to default
 | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	validPayloadWildcardNamespace = JWTAccessTokenClaims{ | ||||||
|  | 		Claims: &jwt.Claims{ | ||||||
|  | 			Issuer:   "http://localhost:3000", | ||||||
|  | 			Subject:  "access-policy:this-uid", | ||||||
|  | 			Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | 			ID:       "1234567890", | ||||||
|  | 			Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
|  | 			IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
|  | 		}, | ||||||
|  | 		Rest: authlib.AccessTokenClaims{ | ||||||
|  | 			Namespace: "*", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	mismatchingNamespaceIDPayload = JWTIDTokenClaims{ | ||||||
|  | 		Claims: &jwt.Claims{ | ||||||
|  | 			Issuer:   "http://localhost:3000", | ||||||
|  | 			Subject:  "user:2", | ||||||
|  | 			Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | 			ID:       "1234567890", | ||||||
|  | 			Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
|  | 			IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
|  | 		}, | ||||||
|  | 		Rest: authlib.IDTokenClaims{ | ||||||
|  | 			AuthenticatedBy: "extended_jwt", | ||||||
|  | 			Namespace:       "org-2", | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 	pk, _ = rsa.GenerateKey(rand.Reader, 4096) | 	pk, _ = rsa.GenerateKey(rand.Reader, 4096) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type mockVerifier struct { | var _ authlib.Verifier[authlib.IDTokenClaims] = &mockIDVerifier{} | ||||||
| 	Claims  []ExtendedJWTClaims | 
 | ||||||
| 	Error   error | type mockIDVerifier struct { | ||||||
| 	counter int | 	Claims JWTIDTokenClaims | ||||||
|  | 	Error  error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *mockVerifier) Verify(ctx context.Context, token string) (*authlib.Claims[ExtendedJWTClaims], error) { | func (m *mockIDVerifier) Verify(ctx context.Context, token string) (*JWTIDTokenClaims, error) { | ||||||
| 	m.counter++ | 	return &m.Claims, m.Error | ||||||
| 	claims := m.Claims[m.counter-1] | } | ||||||
| 	return &authlib.Claims[ExtendedJWTClaims]{ | 
 | ||||||
| 		Claims: &claims.Claims, | var _ authlib.Verifier[authlib.AccessTokenClaims] = &mockVerifier{} | ||||||
| 		Rest:   claims, | 
 | ||||||
| 	}, m.Error | type mockVerifier struct { | ||||||
|  | 	Claims JWTAccessTokenClaims | ||||||
|  | 	Error  error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *mockVerifier) Verify(ctx context.Context, token string) (*JWTAccessTokenClaims, error) { | ||||||
|  | 	return &m.Claims, m.Error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestExtendedJWT_Test(t *testing.T) { | func TestExtendedJWT_Test(t *testing.T) { | ||||||
|  | @ -93,13 +138,13 @@ func TestExtendedJWT_Test(t *testing.T) { | ||||||
| 		{ | 		{ | ||||||
| 			name:           "should return true when Authorization header contains Bearer prefix", | 			name:           "should return true when Authorization header contains Bearer prefix", | ||||||
| 			cfg:            nil, | 			cfg:            nil, | ||||||
| 			authHeaderFunc: func() string { return "Bearer " + generateToken(validPayload, pk, jose.RS256, "at+jwt") }, | 			authHeaderFunc: func() string { return "Bearer " + generateToken(validPayload, pk, jose.RS256) }, | ||||||
| 			want:           true, | 			want:           true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:           "should return true when Authorization header only contains the token", | 			name:           "should return true when Authorization header only contains the token", | ||||||
| 			cfg:            nil, | 			cfg:            nil, | ||||||
| 			authHeaderFunc: func() string { return generateToken(validPayload, pk, jose.RS256, "at+jwt") }, | 			authHeaderFunc: func() string { return generateToken(validPayload, pk, jose.RS256) }, | ||||||
| 			want:           true, | 			want:           true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -124,7 +169,7 @@ func TestExtendedJWT_Test(t *testing.T) { | ||||||
| 			authHeaderFunc: func() string { | 			authHeaderFunc: func() string { | ||||||
| 				payload := validPayload | 				payload := validPayload | ||||||
| 				payload.Issuer = "http://unknown-issuer" | 				payload.Issuer = "http://unknown-issuer" | ||||||
| 				return generateToken(payload, pk, jose.RS256, "at+jwt") | 				return generateToken(payload, pk, jose.RS256) | ||||||
| 			}, | 			}, | ||||||
| 			want: false, | 			want: false, | ||||||
| 		}, | 		}, | ||||||
|  | @ -152,8 +197,8 @@ func TestExtendedJWT_Test(t *testing.T) { | ||||||
| func TestExtendedJWT_Authenticate(t *testing.T) { | func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| 	type testCase struct { | 	type testCase struct { | ||||||
| 		name        string | 		name        string | ||||||
| 		payload     ExtendedJWTClaims | 		payload     *JWTAccessTokenClaims | ||||||
| 		idPayload   *ExtendedJWTClaims | 		idPayload   *JWTIDTokenClaims | ||||||
| 		orgID       int64 | 		orgID       int64 | ||||||
| 		want        *authn.Identity | 		want        *authn.Identity | ||||||
| 		initTestEnv func(env *testEnv) | 		initTestEnv func(env *testEnv) | ||||||
|  | @ -162,7 +207,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| 	testCases := []testCase{ | 	testCases := []testCase{ | ||||||
| 		{ | 		{ | ||||||
| 			name:    "successful authentication as service", | 			name:    "successful authentication as service", | ||||||
| 			payload: validPayload, | 			payload: &validPayload, | ||||||
| 			orgID:   1, | 			orgID:   1, | ||||||
| 			want: &authn.Identity{OrgID: 1, OrgName: "", | 			want: &authn.Identity{OrgID: 1, OrgName: "", | ||||||
| 				OrgRoles: map[int64]roletype.RoleType(nil), | 				OrgRoles: map[int64]roletype.RoleType(nil), | ||||||
|  | @ -182,7 +227,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:      "successful authentication as user", | 			name:      "successful authentication as user", | ||||||
| 			payload:   validPayload, | 			payload:   &validPayload, | ||||||
| 			idPayload: &validIDPayload, | 			idPayload: &validIDPayload, | ||||||
| 			orgID:     1, | 			orgID:     1, | ||||||
| 			initTestEnv: func(env *testEnv) { | 			initTestEnv: func(env *testEnv) { | ||||||
|  | @ -212,10 +257,44 @@ func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| 						Roles: []string(nil)}}, Permissions: map[int64]map[string][]string(nil), IDToken: ""}, | 						Roles: []string(nil)}}, Permissions: map[int64]map[string][]string(nil), IDToken: ""}, | ||||||
| 			wantErr: nil, | 			wantErr: nil, | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:      "fail authentication as user when access token namespace claim doesn't match id token namespace", | ||||||
|  | 			payload:   &validPayload, | ||||||
|  | 			idPayload: &mismatchingNamespaceIDPayload, | ||||||
|  | 			orgID:     1, | ||||||
|  | 			initTestEnv: func(env *testEnv) { | ||||||
|  | 				env.userSvc.ExpectedSignedInUser = &user.SignedInUser{ | ||||||
|  | 					UserID:  2, | ||||||
|  | 					OrgID:   1, | ||||||
|  | 					OrgRole: roletype.RoleAdmin, | ||||||
|  | 					Name:    "John Doe", | ||||||
|  | 					Email:   "johndoe@grafana.com", | ||||||
|  | 					Login:   "johndoe", | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 			wantErr: errJWTMismatchedNamespaceClaims.Errorf("id token namespace: %s, access token namespace: %s", mismatchingNamespaceIDPayload.Rest.Namespace, validPayload.Rest.Namespace), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:      "fail authentication as user when id token namespace claim doesn't match allowed namespace", | ||||||
|  | 			payload:   &validPayloadWildcardNamespace, | ||||||
|  | 			idPayload: &validIDPayload, | ||||||
|  | 			orgID:     1, | ||||||
|  | 			initTestEnv: func(env *testEnv) { | ||||||
|  | 				env.userSvc.ExpectedSignedInUser = &user.SignedInUser{ | ||||||
|  | 					UserID:  2, | ||||||
|  | 					OrgID:   1, | ||||||
|  | 					OrgRole: roletype.RoleAdmin, | ||||||
|  | 					Name:    "John Doe", | ||||||
|  | 					Email:   "johndoe@grafana.com", | ||||||
|  | 					Login:   "johndoe", | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 			wantErr: errJWTDisallowedNamespaceClaim, | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "should return error when the subject is not an access-policy", | 			name: "should return error when the subject is not an access-policy", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "user:2", | 					Subject:  "user:2", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | @ -223,7 +302,9 @@ func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Permissions: []string{"fixed:folders:reader"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Permissions: []string{"fixed:folders:reader"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			orgID:   1, | 			orgID:   1, | ||||||
| 			want:    nil, | 			want:    nil, | ||||||
|  | @ -240,14 +321,15 @@ func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 			validHTTPReq := &http.Request{ | 			validHTTPReq := &http.Request{ | ||||||
| 				Header: map[string][]string{ | 				Header: map[string][]string{ | ||||||
| 					"X-Access-Token": {generateToken(tc.payload, pk, jose.RS256, "at+jwt")}, | 					"X-Access-Token": {generateToken(*tc.payload, pk, jose.RS256)}, | ||||||
| 				}, | 				}, | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			env.s.verifier = &mockVerifier{Claims: []ExtendedJWTClaims{tc.payload}} | 			env.s.accessTokenVerifier = &mockVerifier{Claims: *tc.payload} | ||||||
| 			if tc.idPayload != nil { | 			if tc.idPayload != nil { | ||||||
| 				env.s.verifier = &mockVerifier{Claims: []ExtendedJWTClaims{tc.payload, *tc.idPayload}} | 				env.s.accessTokenVerifier = &mockVerifier{Claims: *tc.payload} | ||||||
| 				validHTTPReq.Header.Add(extJWTAuthorizationHeaderName, generateToken(*tc.idPayload, pk, jose.RS256, "jwt")) | 				env.s.idTokenVerifier = &mockIDVerifier{Claims: *tc.idPayload} | ||||||
|  | 				validHTTPReq.Header.Add(extJWTAuthorizationHeaderName, generateIDToken(*tc.idPayload, pk, jose.RS256)) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			id, err := env.s.Authenticate(context.Background(), &authn.Request{ | 			id, err := env.s.Authenticate(context.Background(), &authn.Request{ | ||||||
|  | @ -268,43 +350,48 @@ func TestExtendedJWT_Authenticate(t *testing.T) { | ||||||
| // https://datatracker.ietf.org/doc/html/rfc9068#name-data-structure
 | // https://datatracker.ietf.org/doc/html/rfc9068#name-data-structure
 | ||||||
| func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| 	type testCase struct { | 	type testCase struct { | ||||||
| 		name    string | 		name             string | ||||||
| 		payload ExtendedJWTClaims | 		payload          *JWTAccessTokenClaims | ||||||
| 		alg     jose.SignatureAlgorithm | 		idPayload        *JWTIDTokenClaims | ||||||
| 		typ     string | 		alg              jose.SignatureAlgorithm | ||||||
|  | 		generateWrongTyp bool | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	testCases := []testCase{ | 	testCases := []testCase{ | ||||||
| 		{ | 		{ | ||||||
| 			name: "missing iss", | 			name: "missing iss", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
| 					ID:       "1234567890", | 					ID:       "1234567890", | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "missing expiry", | 			name: "missing expiry", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
| 					ID:       "1234567890", | 					ID:       "1234567890", | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "expired token", | 			name: "expired token", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | @ -312,26 +399,30 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "missing aud", | 			name: "missing aud", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					ID:       "1234567890", | 					ID:       "1234567890", | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "wrong aud", | 			name: "wrong aud", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://some-other-host:3000"}, | 					Audience: jwt.Audience{"http://some-other-host:3000"}, | ||||||
|  | @ -339,54 +430,50 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "wrong typ", | 			name:             "wrong typ", | ||||||
| 			payload: ExtendedJWTClaims{ | 			idPayload:        &validIDPayload, | ||||||
| 				Claims: jwt.Claims{ | 			generateWrongTyp: true, | ||||||
| 					Issuer:   "http://localhost:3000", |  | ||||||
| 					Subject:  "access-policy:this-uid", |  | ||||||
| 					Audience: jwt.Audience{"http://some-other-host:3000"}, |  | ||||||
| 					ID:       "1234567890", |  | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), |  | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), |  | ||||||
| 				}, |  | ||||||
| 				Scopes: []string{"profile", "groups"}, |  | ||||||
| 			}, |  | ||||||
| 			typ: "jwt", |  | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "missing sub", | 			name: "missing sub", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
| 					ID:       "1234567890", | 					ID:       "1234567890", | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "missing iat", | 			name: "missing iat", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
| 					ID:       "1234567890", | 					ID:       "1234567890", | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "iat later than current time", | 			name: "iat later than current time", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | @ -394,13 +481,15 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 2, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 2, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "unsupported alg", | 			name: "unsupported alg", | ||||||
| 			payload: ExtendedJWTClaims{ | 			payload: &JWTAccessTokenClaims{ | ||||||
| 				Claims: jwt.Claims{ | 				Claims: &jwt.Claims{ | ||||||
| 					Issuer:   "http://localhost:3000", | 					Issuer:   "http://localhost:3000", | ||||||
| 					Subject:  "access-policy:this-uid", | 					Subject:  "access-policy:this-uid", | ||||||
| 					Audience: jwt.Audience{"http://localhost:3000"}, | 					Audience: jwt.Audience{"http://localhost:3000"}, | ||||||
|  | @ -408,7 +497,9 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | 					Expiry:   jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), | ||||||
| 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | 					IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), | ||||||
| 				}, | 				}, | ||||||
| 				Scopes: []string{"profile", "groups"}, | 				Rest: authlib.AccessTokenClaims{ | ||||||
|  | 					Scopes: []string{"profile", "groups"}, | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			alg: jose.RS384, | 			alg: jose.RS384, | ||||||
| 		}, | 		}, | ||||||
|  | @ -421,8 +512,14 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| 			if tc.alg == "" { | 			if tc.alg == "" { | ||||||
| 				tc.alg = jose.RS256 | 				tc.alg = jose.RS256 | ||||||
| 			} | 			} | ||||||
| 			tokenToTest := generateToken(tc.payload, pk, tc.alg, "at+jwt") | 
 | ||||||
| 			_, err := env.s.verifyRFC9068Token(context.Background(), tokenToTest, rfc9068ShortMediaType) | 			var tokenToTest string | ||||||
|  | 			if tc.generateWrongTyp { | ||||||
|  | 				tokenToTest = generateIDToken(*tc.idPayload, pk, tc.alg) | ||||||
|  | 			} else { | ||||||
|  | 				tokenToTest = generateToken(*tc.payload, pk, tc.alg) | ||||||
|  | 			} | ||||||
|  | 			_, err := env.s.accessTokenVerifier.Verify(context.Background(), tokenToTest) | ||||||
| 			require.Error(t, err) | 			require.Error(t, err) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  | @ -431,6 +528,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { | ||||||
| func setupTestCtx(cfg *setting.Cfg) *testEnv { | func setupTestCtx(cfg *setting.Cfg) *testEnv { | ||||||
| 	if cfg == nil { | 	if cfg == nil { | ||||||
| 		cfg = &setting.Cfg{ | 		cfg = &setting.Cfg{ | ||||||
|  | 			// default org set up by the authenticator is 1
 | ||||||
| 			ExtJWTAuth: setting.ExtJWTSettings{ | 			ExtJWTAuth: setting.ExtJWTSettings{ | ||||||
| 				Enabled:        true, | 				Enabled:        true, | ||||||
| 				ExpectIssuer:   "http://localhost:3000", | 				ExpectIssuer:   "http://localhost:3000", | ||||||
|  | @ -459,10 +557,21 @@ type testEnv struct { | ||||||
| 	s       *ExtendedJWT | 	s       *ExtendedJWT | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func generateToken(payload ExtendedJWTClaims, signingKey any, alg jose.SignatureAlgorithm, typ string) string { | func generateToken(payload JWTAccessTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string { | ||||||
| 	signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ | 	signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ | ||||||
| 		ExtraHeaders: map[jose.HeaderKey]any{ | 		ExtraHeaders: map[jose.HeaderKey]any{ | ||||||
| 			jose.HeaderType: typ, | 			jose.HeaderType: authlib.TokenTypeAccess, | ||||||
|  | 			"kid":           "default", | ||||||
|  | 		}}) | ||||||
|  | 
 | ||||||
|  | 	result, _ := jwt.Signed(signer).Claims(payload).CompactSerialize() | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func generateIDToken(payload JWTIDTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string { | ||||||
|  | 	signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ | ||||||
|  | 		ExtraHeaders: map[jose.HeaderKey]any{ | ||||||
|  | 			jose.HeaderType: authlib.TokenTypeID, | ||||||
| 			"kid":           "default", | 			"kid":           "default", | ||||||
| 		}}) | 		}}) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,6 +27,10 @@ var ( | ||||||
| 		"jwt.missing_claim", errutil.WithPublicMessage("Missing mandatory claim in JWT")) | 		"jwt.missing_claim", errutil.WithPublicMessage("Missing mandatory claim in JWT")) | ||||||
| 	errJWTInvalidRole = errutil.Forbidden( | 	errJWTInvalidRole = errutil.Forbidden( | ||||||
| 		"jwt.invalid_role", errutil.WithPublicMessage("Invalid Role in claim")) | 		"jwt.invalid_role", errutil.WithPublicMessage("Invalid Role in claim")) | ||||||
|  | 	errJWTMismatchedNamespaceClaims = errutil.Unauthorized( | ||||||
|  | 		"jwt.namespace_mismatch", errutil.WithPublicMessage("Namespace claims didn't match between id token and access token")) | ||||||
|  | 	errJWTDisallowedNamespaceClaim = errutil.Unauthorized( | ||||||
|  | 		"jwt.namespace_mismatch", errutil.WithPublicMessage("Namespace claim doesn't allow access to requested namespace")) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func ProvideJWT(jwtService auth.JWTVerifierService, cfg *setting.Cfg) *JWT { | func ProvideJWT(jwtService auth.JWTVerifierService, cfg *setting.Cfg) *JWT { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue