mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
feat(authz): add openfga authz middleware (#8990)
* feat(authz): add openfga authz middleware * feat(authz): update the auth context * feat(authz): update the auth context * feat(authz): update check request * feat(authz): update check request * feat(authz): add lifecycle tests * feat(authz): add lifecycle tests * feat(authz): add start-stop tests
This commit is contained in:
parent
7602d863dd
commit
2dbe0777f4
9
go.mod
9
go.mod
@ -87,7 +87,6 @@ require (
|
|||||||
cloud.google.com/go/auth v0.16.1 // indirect
|
cloud.google.com/go/auth v0.16.1 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||||
@ -135,7 +134,6 @@ require (
|
|||||||
github.com/go-openapi/spec v0.21.0 // indirect
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/go-openapi/validate v0.24.0 // indirect
|
github.com/go-openapi/validate v0.24.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
@ -185,7 +183,6 @@ require (
|
|||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||||
github.com/magefile/mage v1.15.0 // indirect
|
github.com/magefile/mage v1.15.0 // indirect
|
||||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mdlayher/socket v0.4.1 // indirect
|
github.com/mdlayher/socket v0.4.1 // indirect
|
||||||
github.com/mdlayher/vsock v1.2.1 // indirect
|
github.com/mdlayher/vsock v1.2.1 // indirect
|
||||||
@ -199,7 +196,6 @@ require (
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
github.com/natefinch/wrap v0.2.0 // indirect
|
github.com/natefinch/wrap v0.2.0 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
|
||||||
github.com/oklog/run v1.1.0 // indirect
|
github.com/oklog/run v1.1.0 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||||
@ -221,7 +217,6 @@ require (
|
|||||||
github.com/prometheus/procfs v0.16.1 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
github.com/prometheus/sigv4 v0.1.2 // indirect
|
github.com/prometheus/sigv4 v0.1.2 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||||
@ -330,9 +325,5 @@ require (
|
|||||||
k8s.io/client-go v0.33.0 // indirect
|
k8s.io/client-go v0.33.0 // indirect
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
||||||
modernc.org/libc v1.66.3 // indirect
|
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
|
||||||
modernc.org/memory v1.11.0 // indirect
|
|
||||||
modernc.org/sqlite v1.38.2 // indirect
|
|
||||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
21
go.sum
21
go.sum
@ -443,8 +443,6 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
|||||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||||
@ -1466,7 +1464,6 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
@ -1793,32 +1790,14 @@ k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUy
|
|||||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
||||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
|
||||||
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
|
||||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
|
||||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
|
||||||
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
|
|
||||||
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
|
||||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
|
||||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
|
||||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
|
||||||
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
|
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
|
||||||
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
|
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
|
||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
|
||||||
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
||||||
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
|
||||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|||||||
@ -1,7 +1,15 @@
|
|||||||
package authz
|
package authz
|
||||||
|
|
||||||
import "github.com/SigNoz/signoz/pkg/factory"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
|
)
|
||||||
|
|
||||||
type AuthZ interface {
|
type AuthZ interface {
|
||||||
factory.Service
|
factory.Service
|
||||||
|
|
||||||
|
// Check returns error when the upstream authorization server is unavailable or the subject (s) doesn't have relation (r) on object (o).
|
||||||
|
Check(context.Context, *openfgav1.CheckRequestTupleKey) error
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,12 @@ package openfgaauthz
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
authz "github.com/SigNoz/signoz/pkg/authz"
|
authz "github.com/SigNoz/signoz/pkg/authz"
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
@ -13,25 +17,31 @@ import (
|
|||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
openfgaDefaultStore = valuer.NewString("signoz")
|
||||||
|
)
|
||||||
|
|
||||||
type provider struct {
|
type provider struct {
|
||||||
config authz.Config
|
config authz.Config
|
||||||
settings factory.ScopedProviderSettings
|
settings factory.ScopedProviderSettings
|
||||||
openfgaSchema []openfgapkgtransformer.ModuleFile
|
openfgaSchema []openfgapkgtransformer.ModuleFile
|
||||||
openfgaServer *openfgapkgserver.Server
|
openfgaServer *openfgapkgserver.Server
|
||||||
|
storeID string
|
||||||
|
modelID string
|
||||||
|
mtx sync.RWMutex
|
||||||
stopChan chan struct{}
|
stopChan chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProviderFactory(sqlstoreConfig sqlstore.Config, openfgaSchema []openfgapkgtransformer.ModuleFile) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
func NewProviderFactory(sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
||||||
return factory.NewProviderFactory(factory.MustNewName("openfga"), func(ctx context.Context, ps factory.ProviderSettings, config authz.Config) (authz.AuthZ, error) {
|
return factory.NewProviderFactory(factory.MustNewName("openfga"), func(ctx context.Context, ps factory.ProviderSettings, config authz.Config) (authz.AuthZ, error) {
|
||||||
return newOpenfgaProvider(ctx, ps, config, sqlstoreConfig, openfgaSchema)
|
return newOpenfgaProvider(ctx, ps, config, sqlstore, openfgaSchema)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings, config authz.Config, sqlstoreConfig sqlstore.Config, openfgaSchema []openfgapkgtransformer.ModuleFile) (authz.AuthZ, error) {
|
func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings, config authz.Config, sqlstore sqlstore.SQLStore, openfgaSchema []openfgapkgtransformer.ModuleFile) (authz.AuthZ, error) {
|
||||||
scopedProviderSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/pkg/authz/openfgaauthz")
|
scopedProviderSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/pkg/authz/openfgaauthz")
|
||||||
|
|
||||||
// setup connections and run the migrations
|
store, err := NewSQLStore(sqlstore)
|
||||||
sqlstore, err := NewSQLStore(storeConfig{sqlstoreConfig: sqlstoreConfig})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scopedProviderSettings.Logger().DebugContext(ctx, "failed to initialize sqlstore for authz")
|
scopedProviderSettings.Logger().DebugContext(ctx, "failed to initialize sqlstore for authz")
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -39,7 +49,7 @@ func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings,
|
|||||||
|
|
||||||
// setup the openfga server
|
// setup the openfga server
|
||||||
opts := []openfgapkgserver.OpenFGAServiceV1Option{
|
opts := []openfgapkgserver.OpenFGAServiceV1Option{
|
||||||
openfgapkgserver.WithDatastore(sqlstore),
|
openfgapkgserver.WithDatastore(store),
|
||||||
openfgapkgserver.WithLogger(NewLogger(scopedProviderSettings.Logger())),
|
openfgapkgserver.WithLogger(NewLogger(scopedProviderSettings.Logger())),
|
||||||
}
|
}
|
||||||
openfgaServer, err := openfgapkgserver.NewServerWithOpts(opts...)
|
openfgaServer, err := openfgapkgserver.NewServerWithOpts(opts...)
|
||||||
@ -53,21 +63,27 @@ func newOpenfgaProvider(ctx context.Context, settings factory.ProviderSettings,
|
|||||||
settings: scopedProviderSettings,
|
settings: scopedProviderSettings,
|
||||||
openfgaServer: openfgaServer,
|
openfgaServer: openfgaServer,
|
||||||
openfgaSchema: openfgaSchema,
|
openfgaSchema: openfgaSchema,
|
||||||
|
mtx: sync.RWMutex{},
|
||||||
stopChan: make(chan struct{}),
|
stopChan: make(chan struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Start(ctx context.Context) error {
|
func (provider *provider) Start(ctx context.Context) error {
|
||||||
storeId, err := provider.getOrCreateStore(ctx, "signoz")
|
storeId, err := provider.getOrCreateStore(ctx, openfgaDefaultStore.StringValue())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = provider.getOrCreateModel(ctx, storeId)
|
modelID, err := provider.getOrCreateModel(ctx, storeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider.mtx.Lock()
|
||||||
|
provider.modelID = modelID
|
||||||
|
provider.storeID = storeId
|
||||||
|
provider.mtx.Unlock()
|
||||||
|
|
||||||
<-provider.stopChan
|
<-provider.stopChan
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -157,4 +173,24 @@ func (provider *provider) isModelEqual(expected *openfgav1.AuthorizationModel, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
return string(expectedAuthModelBytes) == string(actualAuthModelBytes), nil
|
return string(expectedAuthModelBytes) == string(actualAuthModelBytes), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Check(ctx context.Context, tupleReq *openfgav1.CheckRequestTupleKey) error {
|
||||||
|
checkResponse, err := provider.openfgaServer.Check(
|
||||||
|
ctx,
|
||||||
|
&openfgav1.CheckRequest{
|
||||||
|
StoreId: provider.storeID,
|
||||||
|
AuthorizationModelId: provider.modelID,
|
||||||
|
TupleKey: tupleReq,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkResponse.Allowed {
|
||||||
|
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subject %s cannot %s object %s", tupleReq.User, tupleReq.Relation, tupleReq.Object)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
47
pkg/authz/openfgaauthz/provider_test.go
Normal file
47
pkg/authz/openfgaauthz/provider_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package openfgaauthz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
|
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest"
|
||||||
|
"github.com/openfga/language/pkg/go/transformer"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProviderStartStop(t *testing.T) {
|
||||||
|
providerSettings := instrumentationtest.New().ToProviderSettings()
|
||||||
|
sqlstore := sqlstoretest.New(sqlstore.Config{Provider: "postgres"}, sqlmock.QueryMatcherRegexp)
|
||||||
|
|
||||||
|
expectedModel := `module base
|
||||||
|
type user`
|
||||||
|
provider, err := newOpenfgaProvider(context.Background(), providerSettings, authz.Config{}, sqlstore, []transformer.ModuleFile{{Name: "test.fga", Contents: expectedModel}})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storeRows := sqlstore.Mock().NewRows([]string{"id", "name", "created_at", "updated_at"}).AddRow("01K3V0NTN47MPTMEV1PD5ST6ZC", "signoz", time.Now(), time.Now())
|
||||||
|
sqlstore.Mock().ExpectQuery("SELECT (.+) FROM store WHERE (.+)").WillReturnRows(storeRows)
|
||||||
|
|
||||||
|
authModelCollectionRows := sqlstore.Mock().NewRows([]string{"authorization_model_id"}).AddRow("01K44QQKXR6F729W160NFCJT58")
|
||||||
|
sqlstore.Mock().ExpectQuery("SELECT DISTINCT (.+) FROM authorization_model WHERE store (.+) ORDER BY (.+)").WillReturnRows(authModelCollectionRows)
|
||||||
|
|
||||||
|
modelRows := sqlstore.Mock().NewRows([]string{"authorization_model_id", "schema_version", "type", "type_definition", "serialized_protobuf"}).
|
||||||
|
AddRow("01K44QQKXR6F729W160NFCJT58", "1.1", "", "", "")
|
||||||
|
sqlstore.Mock().ExpectQuery("SELECT authorization_model_id, schema_version, type, type_definition, serialized_protobuf FROM authorization_model WHERE authorization_model_id = (.+) AND store = (.+)").WithArgs("01K44QQKXR6F729W160NFCJT58", "01K3V0NTN47MPTMEV1PD5ST6ZC").WillReturnRows(modelRows)
|
||||||
|
|
||||||
|
sqlstore.Mock().ExpectExec("INSERT INTO authorization_model (.+) VALUES (.+)").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
go func() {
|
||||||
|
err := provider.Start(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait for the service to start
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
|
||||||
|
err = provider.Stop(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
@ -4,39 +4,19 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/errors"
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/openfga/openfga/pkg/storage"
|
"github.com/openfga/openfga/pkg/storage"
|
||||||
"github.com/openfga/openfga/pkg/storage/migrate"
|
|
||||||
"github.com/openfga/openfga/pkg/storage/postgres"
|
"github.com/openfga/openfga/pkg/storage/postgres"
|
||||||
"github.com/openfga/openfga/pkg/storage/sqlcommon"
|
"github.com/openfga/openfga/pkg/storage/sqlcommon"
|
||||||
"github.com/openfga/openfga/pkg/storage/sqlite"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type storeConfig struct {
|
func NewSQLStore(sqlstore sqlstore.SQLStore) (storage.OpenFGADatastore, error) {
|
||||||
sqlstoreConfig sqlstore.Config
|
switch sqlstore.BunDB().Dialect().Name().String() {
|
||||||
}
|
// use the NewWithDB for sqlite once https://github.com/openfga/openfga/pull/2679 is merged and released else will figure out something else.
|
||||||
|
|
||||||
func NewSQLStore(cfg storeConfig) (storage.OpenFGADatastore, error) {
|
|
||||||
switch cfg.sqlstoreConfig.Provider {
|
|
||||||
case "sqlite":
|
case "sqlite":
|
||||||
err := migrate.RunMigrations(migrate.MigrationConfig{Engine: cfg.sqlstoreConfig.Provider, URI: "file:" + cfg.sqlstoreConfig.Sqlite.Path + "?_foreign_keys=true"})
|
case "pg":
|
||||||
if err != nil {
|
return postgres.NewWithDB(sqlstore.SQLDB(), nil, &sqlcommon.Config{
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqlite.New("file:"+cfg.sqlstoreConfig.Sqlite.Path+"?_foreign_keys=true", &sqlcommon.Config{
|
|
||||||
MaxTuplesPerWriteField: 100,
|
MaxTuplesPerWriteField: 100,
|
||||||
MaxTypesPerModelField: 100,
|
MaxTypesPerModelField: 100,
|
||||||
})
|
})
|
||||||
case "postgres":
|
|
||||||
err := migrate.RunMigrations(migrate.MigrationConfig{Engine: cfg.sqlstoreConfig.Provider, URI: cfg.sqlstoreConfig.Postgres.DSN})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return postgres.New(cfg.sqlstoreConfig.Postgres.DSN, &sqlcommon.Config{
|
|
||||||
MaxTuplesPerWriteField: 100,
|
|
||||||
MaxTypesPerModelField: 100,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid store type: %s", cfg.sqlstoreConfig.Provider)
|
|
||||||
}
|
}
|
||||||
|
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid store type: %s", sqlstore.BunDB().Dialect().Name().String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
"github.com/SigNoz/signoz/pkg/http/render"
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@ -14,7 +15,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AuthZ struct {
|
type AuthZ struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
authzService authz.AuthZ
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthZ(logger *slog.Logger) *AuthZ {
|
func NewAuthZ(logger *slog.Logger) *AuthZ {
|
||||||
@ -103,3 +105,17 @@ func (middleware *AuthZ) OpenAccess(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
next(rw, req)
|
next(rw, req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// each individual APIs should be responsible for defining the relation and the object being accessed, subject will be derived from the request
|
||||||
|
func (middleware *AuthZ) Check(next http.HandlerFunc, relation string) http.HandlerFunc {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
checkRequestTupleKey := authtypes.NewTuple("", "", "")
|
||||||
|
err := middleware.authzService.Check(req.Context(), checkRequestTupleKey)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next(rw, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
15
pkg/types/authtypes/tuple.go
Normal file
15
pkg/types/authtypes/tuple.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package authtypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCodeAuthZUnavailable = errors.MustNewCode("authz_unavailable")
|
||||||
|
ErrCodeAuthZForbidden = errors.MustNewCode("authz_forbidden")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTuple(subject string, relation string, object string) *openfgav1.CheckRequestTupleKey {
|
||||||
|
return &openfgav1.CheckRequestTupleKey{User: subject, Relation: relation, Object: object}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user