From bebc7f30768a68e1d02c7e7c4e4ff3301b1db55f Mon Sep 17 00:00:00 2001 From: Vyacheslav1557 Date: Tue, 20 Aug 2024 16:18:23 +0500 Subject: [PATCH] feat: add permission check --- go.mod | 51 ++++++--- go.sum | 158 ++++++++++++++++++++++---- internal/models/role.go | 4 + internal/models/user.go | 9 ++ internal/services/permission.go | 39 +++++++ internal/services/user.go | 29 +++++ internal/storage/contests.go | 22 ++-- internal/storage/errhandling.go | 14 +-- internal/storage/user.go | 42 +++++++ internal/transport/interceptors.go | 110 +++++++++++++++--- internal/transport/problem.go | 25 ++-- internal/transport/server.go | 20 +++- migrations/20240727123308_initial.sql | 18 +++ opa/problem.rego | 44 +++++++ 14 files changed, 490 insertions(+), 95 deletions(-) create mode 100644 internal/models/user.go create mode 100644 internal/services/permission.go create mode 100644 internal/services/user.go create mode 100644 internal/storage/user.go create mode 100644 opa/problem.rego diff --git a/go.mod b/go.mod index 61284c0..818aab1 100644 --- a/go.mod +++ b/go.mod @@ -3,32 +3,55 @@ module git.sch9.ru/new_gate/ms-tester go 1.19 require ( - github.com/google/uuid v1.6.0 + git.sch9.ru/new_gate/ms-auth v0.0.0-20240816164331-331e413afa6a github.com/ilyakaznacheev/cleanenv v1.5.0 - github.com/valkey-io/valkey-go v1.0.38 - golang.org/x/crypto v0.24.0 - google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.1 + github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 + github.com/open-policy-agent/opa v0.67.1 + go.uber.org/zap v1.27.0 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.2 ) require ( - github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/yashtewari/glob-intersection v0.2.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) require ( github.com/BurntSushi/toml v1.2.1 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/jackc/pgx/v5 v5.6.0 github.com/jmoiron/sqlx v1.4.0 github.com/joho/godotenv v1.5.1 // indirect diff --git a/go.sum b/go.sum index 3c1c147..cfaf862 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,77 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +git.sch9.ru/new_gate/ms-auth v0.0.0-20240816164331-331e413afa6a h1:Gvi3+Qh2VW3iWG3TZWQLjkWeW/4gb621SLLr9BroOro= +git.sch9.ru/new_gate/ms-auth v0.0.0-20240816164331-331e413afa6a/go.mod h1:BL4MiDqgmLngmQqUcKQ1cK1XCJr9DlAOpWnrlGidDdE= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= +github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= @@ -23,58 +82,111 @@ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= -github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/open-policy-agent/opa v0.67.1 h1:rzy26J6g1X+CKknAcx0Vfbt41KqjuSzx4E0A8DAZf3E= +github.com/open-policy-agent/opa v0.67.1/go.mod h1:aqKlHc8E2VAAylYE9x09zJYr/fYzGX+JKne89UGqFzk= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/valkey-io/valkey-go v1.0.38 h1:0g+ozx+WUGmu18h8SVGoIyxdWp820utVLlFkGnQ3h0c= -github.com/valkey-io/valkey-go v1.0.38/go.mod h1:LXqAbjygRuA1YRocojTslAGx2dQB4p8feaseGviWka4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= +github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/models/role.go b/internal/models/role.go index 4ef0634..f3ec8a6 100644 --- a/internal/models/role.go +++ b/internal/models/role.go @@ -42,3 +42,7 @@ func (role Role) Valid() error { } return lib.ErrBadRole } + +func (role Role) AsPointer() *Role { + return &role +} diff --git a/internal/models/user.go b/internal/models/user.go new file mode 100644 index 0000000..357600c --- /dev/null +++ b/internal/models/user.go @@ -0,0 +1,9 @@ +package models + +import "time" + +type User struct { + UserId *int32 `json:"user_id" db:"user_id"` + Role *Role `json:"role" db:"role"` + UpdatedAt *time.Time `json:"updated_at" db:"updated_at"` +} diff --git a/internal/services/permission.go b/internal/services/permission.go new file mode 100644 index 0000000..10a937f --- /dev/null +++ b/internal/services/permission.go @@ -0,0 +1,39 @@ +package services + +import ( + "context" + "git.sch9.ru/new_gate/ms-tester/internal/models" + "github.com/open-policy-agent/opa/rego" +) + +type PermissionService struct { + query *rego.PreparedEvalQuery +} + +func NewPermissionService() *PermissionService { + query, err := rego.New( + rego.Query("allow = data.problem.rbac.allow"), + rego.Load([]string{"./opa/problem.rego"}, nil), + ).PrepareForEval(context.TODO()) + + if err != nil { + panic(err) + } + + return &PermissionService{ + query: &query, + } +} + +func (s *PermissionService) Allowed(ctx context.Context, user *models.User, action string) bool { + input := map[string]interface{}{ + "user": user, + "action": action, + } + + result, err := s.query.Eval(ctx, rego.EvalInput(input)) + if err != nil { + panic(err) + } + return result[0].Bindings["allow"].(bool) +} diff --git a/internal/services/user.go b/internal/services/user.go new file mode 100644 index 0000000..40f1179 --- /dev/null +++ b/internal/services/user.go @@ -0,0 +1,29 @@ +package services + +import ( + "context" + "git.sch9.ru/new_gate/ms-tester/internal/models" +) + +type UserStorage interface { + CreateUser(ctx context.Context, user *models.User) error + ReadUserById(ctx context.Context, userId int32) (*models.User, error) +} + +type UserService struct { + userStorage UserStorage +} + +func NewUserService(userStorage UserStorage) *UserService { + return &UserService{ + userStorage: userStorage, + } +} + +func (s *UserService) CreateUser(ctx context.Context, user *models.User) error { + return s.userStorage.CreateUser(ctx, user) +} + +func (s *UserService) ReadUserById(ctx context.Context, userId int32) (*models.User, error) { + return s.userStorage.ReadUserById(ctx, userId) +} diff --git a/internal/storage/contests.go b/internal/storage/contests.go index 3891b9f..a985ebf 100644 --- a/internal/storage/contests.go +++ b/internal/storage/contests.go @@ -2,14 +2,9 @@ package storage import ( "context" - "errors" - "git.sch9.ru/new_gate/ms-auth/internal/lib" - "git.sch9.ru/new_gate/ms-auth/internal/models" - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v5/pgconn" + "git.sch9.ru/new_gate/ms-tester/internal/models" "github.com/jmoiron/sqlx" "go.uber.org/zap" - "time" ) type ContestStorage struct { @@ -35,17 +30,17 @@ RETURNING id rows, err := storage.db.QueryxContext( ctx, query, - contest.Name + contest.Name, ) if err != nil { - return 0, storage.HandlePgErr(err) + return 0, handlePgErr(err) } defer rows.Close() var id int32 err = rows.StructScan(&id) if err != nil { - return 0, storage.HandlePgErr(err) + return 0, handlePgErr(err) } return id, nil @@ -57,16 +52,16 @@ func (storage *ContestStorage) ReadContestById(ctx context.Context, id int32) (* query := storage.db.Rebind("SELECT * from contests WHERE id=? LIMIT 1") err := storage.db.GetContext(ctx, &contest, query, id) if err != nil { - return nil, storage.HandlePgErr(err) + return nil, handlePgErr(err) } return &contest, nil } -func (storage *ContestStorage) UpdateContest(ctx context.Context, id int32,contest models.Contest) error { +func (storage *ContestStorage) UpdateContest(ctx context.Context, id int32, contest models.Contest) error { query := storage.db.Rebind("UPDATE contests SET name=?") _, err := storage.db.ExecContext(ctx, query, contest.Name) if err != nil { - return storage.HandlePgErr(err) + return handlePgErr(err) } } @@ -74,9 +69,8 @@ func (storage *ContestStorage) DeleteContest(ctx context.Context, id int32) erro query := storage.db.Rebind("DELETE FROM contests WHERE id=?") _, err := storage.db.ExecContext(ctx, query, id) if err != nil { - return storage.HandlePgErr(err) + return handlePgErr(err) } return nil } - diff --git a/internal/storage/errhandling.go b/internal/storage/errhandling.go index 2104940..af8d419 100644 --- a/internal/storage/errhandling.go +++ b/internal/storage/errhandling.go @@ -1,27 +1,21 @@ package storage import ( - "context" "errors" - "git.sch9.ru/new_gate/ms-auth/internal/lib" - "git.sch9.ru/new_gate/ms-auth/internal/models" + "git.sch9.ru/new_gate/ms-tester/internal/lib" "github.com/jackc/pgerrcode" "github.com/jackc/pgx/v5/pgconn" - "github.com/jmoiron/sqlx" - "go.uber.org/zap" - "time" ) -func (storage *UserStorage) HandlePgErr(err error) error { +func handlePgErr(err error) error { var pgErr *pgconn.PgError if !errors.As(err, &pgErr) { - storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error())) + //storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error())) return lib.ErrUnexpected } if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) { return errors.New("unique key violation") // FIXME } - storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error())) + //storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error())) return lib.ErrInternal } - diff --git a/internal/storage/user.go b/internal/storage/user.go new file mode 100644 index 0000000..21957fb --- /dev/null +++ b/internal/storage/user.go @@ -0,0 +1,42 @@ +package storage + +import ( + "context" + "git.sch9.ru/new_gate/ms-tester/internal/models" + "github.com/jmoiron/sqlx" +) + +type UserStorage struct { + db *sqlx.DB +} + +func NewUserStorage(db *sqlx.DB) *UserStorage { + return &UserStorage{ + db: db, + } +} + +func (storage *UserStorage) CreateUser(ctx context.Context, user *models.User) error { + query := storage.db.Rebind("INSERT INTO users (user_id, role) VALUES (?, ?)") + _, err := storage.db.ExecContext(ctx, query, user.UserId, user.Role) + if err != nil { + return err + } + return nil +} + +func (storage *UserStorage) ReadUserById(ctx context.Context, userId int32) (*models.User, error) { + query := storage.db.Rebind(` +SELECT * +FROM users +WHERE user_id = ? +LIMIT 1; +`) + + var user models.User + err := storage.db.GetContext(ctx, &user, query, userId) + if err != nil { + return nil, err + } + return &user, nil +} diff --git a/internal/transport/interceptors.go b/internal/transport/interceptors.go index 5dd2b75..a508f19 100644 --- a/internal/transport/interceptors.go +++ b/internal/transport/interceptors.go @@ -2,36 +2,112 @@ package transport import ( "context" + "errors" + "git.sch9.ru/new_gate/ms-tester/internal/lib" + "git.sch9.ru/new_gate/ms-tester/internal/models" sessionv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/session/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) -type ReqWithToken interface { - GetToken() string +var defaultUser = &models.User{ + UserId: nil, + Role: models.RoleSpectator.AsPointer(), + UpdatedAt: nil, } -func (s *TesterServer) AuthInterceptor() grpc.UnaryServerInterceptor { - return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - reqWithToken, ok := req.(ReqWithToken) - if !ok { - return nil, status.Errorf(codes.Unauthenticated, "") // FIXME +func extractToken(ctx context.Context) (string, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return "", errors.New("no metadata") // FIXME + } + tokens := md.Get("token") + + if len(tokens) == 0 { + return "", errors.New("no token in metadata") // FIXME + } + + token := tokens[0] + if token == "" { + return "", errors.New("empty token in metadata") // FIXME + } + return token, nil +} + +func (s *TesterServer) readSessionAndReadUser(ctx context.Context, token string) (*models.User, error) { + // FIXME: possible bottle neck: should we cache it? (think of it in future) + // FIXME: maybe use single connection instead of multiple requests + userId, err := s.sessionClient.Read(ctx, &sessionv1.ReadSessionRequest{Token: token}) + if err != nil { + return nil, status.Errorf(codes.Unauthenticated, "") // FIXME + } + + user, err := s.userService.ReadUserById(ctx, userId.GetUserId()) // FIXME: must be cached! + if err != nil { + // FIXME: if error is "not found" (when error codes module is written) + // means user has no record, so we should create it + user = &models.User{ + UserId: lib.AsInt32P(userId.GetUserId()), + Role: models.RoleParticipant.AsPointer(), } - - token := reqWithToken.GetToken() - - if token == "" { - return nil, status.Errorf(codes.Unauthenticated, "") // FIXME - } - - userId, err := s.sessionClient.Read(ctx, &sessionv1.ReadSessionRequest{Token: token}) + err = s.userService.CreateUser(ctx, user) if err != nil { return nil, status.Errorf(codes.Unauthenticated, "") // FIXME } + } - ctx = context.WithValue(ctx, "user_id", userId) + return user, nil +} - return handler(ctx, req) +func insertUser(ctx context.Context, user *models.User) context.Context { + return context.WithValue(ctx, "user", user) +} + +func extractUser(ctx context.Context) *models.User { + return ctx.Value("user").(*models.User) +} + +func (s *TesterServer) AuthUnaryInterceptor() grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + token, err := extractToken(ctx) + if err != nil { + return handler(insertUser(ctx, defaultUser), req) + } + + user, err := s.readSessionAndReadUser(ctx, token) + if err != nil { + return handler(insertUser(ctx, defaultUser), req) + } + + return handler(insertUser(ctx, user), req) + } +} + +type ssWrapper struct { + grpc.ServerStream + ctx context.Context +} + +func (s *ssWrapper) Context() context.Context { + return s.ctx +} + +func (s *TesterServer) AuthStreamInterceptor() grpc.StreamServerInterceptor { + return func(server interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + ctx := ss.Context() + + token, err := extractToken(ctx) + if err != nil { + return handler(server, &ssWrapper{ServerStream: ss, ctx: insertUser(ctx, defaultUser)}) + } + + user, err := s.readSessionAndReadUser(ctx, token) + if err != nil { + return handler(server, &ssWrapper{ServerStream: ss, ctx: insertUser(ctx, defaultUser)}) + } + + return handler(server, &ssWrapper{ServerStream: ss, ctx: insertUser(ctx, user)}) } } diff --git a/internal/transport/problem.go b/internal/transport/problem.go index 3b84104..259d7ff 100644 --- a/internal/transport/problem.go +++ b/internal/transport/problem.go @@ -5,7 +5,6 @@ import ( "git.sch9.ru/new_gate/ms-tester/internal/lib" "git.sch9.ru/new_gate/ms-tester/internal/models" problemv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/problem/v1" - sessionv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/session/v1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" @@ -15,22 +14,11 @@ import ( func (s *TesterServer) CreateProblem(server problemv1.ProblemService_CreateProblemServer) error { ctx := server.Context() - req, err := server.Recv() // receive token - if err != nil { - return err // FIXME + if !s.permissionService.Allowed(ctx, extractUser(ctx), "create") { + return status.Errorf(codes.PermissionDenied, "") // FIXME } - token := req.GetToken() - userId, err := s.sessionClient.Read(ctx, &sessionv1.ReadSessionRequest{ - Token: token, - }) - if err != nil { - return err // FIXME - } - - ctx = context.WithValue(ctx, "user_id", userId.GetUserId()) - - req, err = server.Recv() // receive problem + req, err := server.Recv() // receive problem if err != nil { return err // FIXME } @@ -97,6 +85,10 @@ func readChunks(ctx context.Context, server problemv1.ProblemService_CreateProbl } func (s *TesterServer) ReadProblem(ctx context.Context, req *problemv1.ReadProblemRequest) (*problemv1.ReadProblemResponse, error) { + if !s.permissionService.Allowed(ctx, extractUser(ctx), "read") { + return nil, status.Errorf(codes.PermissionDenied, "") // FIXME + } + problem, err := s.problemService.ReadProblemById(ctx, req.GetId()) if err != nil { return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME @@ -137,6 +129,9 @@ func (s *TesterServer) ReadProblem(ctx context.Context, req *problemv1.ReadProbl //} func (s *TesterServer) DeleteProblem(ctx context.Context, req *problemv1.DeleteProblemRequest) (*emptypb.Empty, error) { + if !s.permissionService.Allowed(ctx, extractUser(ctx), "delete") { + return nil, status.Errorf(codes.PermissionDenied, "") // FIXME + } err := s.problemService.DeleteProblem(ctx, req.GetId()) if err != nil { return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME diff --git a/internal/transport/server.go b/internal/transport/server.go index 809d488..4023663 100644 --- a/internal/transport/server.go +++ b/internal/transport/server.go @@ -13,7 +13,7 @@ import ( ) type ProblemService interface { - CreateProblem(ctx context.Context, problem *models.Problem, ch <-chan []byte) (int32, error) // FIXME: specify chan type + CreateProblem(ctx context.Context, problem *models.Problem, ch <-chan []byte) (int32, error) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) UpdateProblem(ctx context.Context, problem *models.Problem) error DeleteProblem(ctx context.Context, id int32) error @@ -26,11 +26,23 @@ type SessionClient interface { ) (*sessionv1.ReadSessionResponse, error) } +type UserService interface { + CreateUser(ctx context.Context, user *models.User) error + ReadUserById(ctx context.Context, userId int32) (*models.User, error) +} + +type PermissionService interface { + Allowed(ctx context.Context, user *models.User, action string) bool +} + type TesterServer struct { problemv1.UnimplementedProblemServiceServer problemService ProblemService sessionClient SessionClient + userService UserService + + permissionService PermissionService grpcServer *grpc.Server logger *zap.Logger @@ -39,16 +51,20 @@ type TesterServer struct { func NewTesterServer( problemService ProblemService, sessionClient SessionClient, + + userService UserService, logger *zap.Logger, ) *TesterServer { server := &TesterServer{ problemService: problemService, sessionClient: sessionClient, + userService: userService, logger: logger, } grpcServer := grpc.NewServer( - grpc.UnaryInterceptor(server.AuthInterceptor()), + grpc.UnaryInterceptor(server.AuthUnaryInterceptor()), + grpc.StreamInterceptor(server.AuthStreamInterceptor()), ) problemv1.RegisterProblemServiceServer(grpcServer, server) diff --git a/migrations/20240727123308_initial.sql b/migrations/20240727123308_initial.sql index 388698d..069a39a 100644 --- a/migrations/20240727123308_initial.sql +++ b/migrations/20240727123308_initial.sql @@ -210,6 +210,22 @@ CREATE TABLE IF NOT EXISTS participant_task penalty INT NOT NULL ); + + + +CREATE TABLE IF NOT EXISTS users +( + user_id INT NOT NULL, + role INT NOT NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + CHECK ( role BETWEEN 0 AND 3), + PRIMARY KEY (user_id) +); + + + + CREATE FUNCTION on_new_participant() RETURNS TRIGGER AS $$ BEGIN --RAISE NOTICE 'NEW.ID:%, NEW.contest_id:%', NEW.id,NEW.contest_id; @@ -272,6 +288,7 @@ $$; CREATE TRIGGER languages_upd_trg BEFORE UPDATE ON languages FOR EACH ROW EXECUTE FUNCTION updated_at_update(); CREATE TRIGGER problems_upd_trg BEFORE UPDATE ON problems FOR EACH ROW EXECUTE FUNCTION updated_at_update(); CREATE TRIGGER contests_upd_trg BEFORE UPDATE ON contests FOR EACH ROW EXECUTE FUNCTION updated_at_update(); +CREATE TRIGGER users_upd_trg BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION updated_at_update(); -- +goose StatementEnd -- +goose Down @@ -295,4 +312,5 @@ DROP TABLE IF EXISTS subtasks CASCADE; DROP TABLE IF EXISTS participants CASCADE; DROP TABLE IF EXISTS participant_task CASCADE; DROP TABLE IF EXISTS participant_subtask CASCADE; +DROP TABLE IF EXISTS users CASCADE; -- +goose StatementEnd diff --git a/opa/problem.rego b/opa/problem.rego new file mode 100644 index 0000000..8525c6a --- /dev/null +++ b/opa/problem.rego @@ -0,0 +1,44 @@ +package problem.rbac + +import rego.v1 + +spectator := 0 +participant := 1 +moderator := 2 +admin := 3 + +permissions := { + "read": is_spectator, + "participate": is_participant, + "update": is_moderator, + "create": is_moderator, + "delete": is_moderator, +} + +default allow := false + +allow if is_admin + +allow if { + permissions[input.action] +} + +default is_admin := false +is_admin if { + input.user.role == admin +} + +default is_moderator := false +is_moderator if { + input.user.role >= moderator +} + +default is_participant := false +is_participant if { + input.user.role >= participant +} + +default is_spectator := true +is_spectator if { + input.user.role >= spectator +}