diff options
| author | Max Resnick <max@ofmax.li> | 2020-11-08 11:45:16 -0800 |
|---|---|---|
| committer | Max Resnick <max@ofmax.li> | 2021-01-01 10:50:14 -0800 |
| commit | a397341ad471cc761f7fb930d77e53cf7eb40a2a (patch) | |
| tree | 76fb8318269569687fdd30467dc61ecba3499d09 | |
| parent | 689a57ec4a444f8233fe2e5ec7ceb0903218218d (diff) | |
| download | iserv-a397341ad471cc761f7fb930d77e53cf7eb40a2a.tar.gz | |
adds casbin and accounts
| -rw-r--r-- | auth_model.ini | 15 | ||||
| -rw-r--r-- | cmd/web/main.go | 22 | ||||
| -rw-r--r-- | go.mod | 8 | ||||
| -rw-r--r-- | go.sum | 100 | ||||
| -rw-r--r-- | internal/acct/acct.go | 106 | ||||
| -rw-r--r-- | internal/auth/handler.go | 35 | ||||
| -rw-r--r-- | internal/auth/middleware.go | 45 | ||||
| -rw-r--r-- | internal/auth/model.go | 15 | ||||
| -rw-r--r-- | internal/auth/repo.go | 13 | ||||
| -rw-r--r-- | internal/auth/service.go | 36 | ||||
| -rw-r--r-- | internal/auth/service_test.go | 3 | ||||
| -rw-r--r-- | internal/db/redis/acct.go | 44 | ||||
| -rw-r--r-- | internal/db/redis/auth.go | 51 | ||||
| -rw-r--r-- | internal/db/redis/image.go | 2 | ||||
| -rw-r--r-- | internal/goog/goog.go | 89 | ||||
| -rw-r--r-- | internal/image/handler.go | 5 | ||||
| -rw-r--r-- | internal/image/service.go | 1 | ||||
| -rw-r--r-- | policy.csv | 6 |
18 files changed, 528 insertions, 68 deletions
diff --git a/auth_model.ini b/auth_model.ini new file mode 100644 index 0000000..44507a6 --- /dev/null +++ b/auth_model.ini @@ -0,0 +1,15 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ +g2 = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && g2(r.obj, p.obj) && regexMatch(r.act, p.act) diff --git a/cmd/web/main.go b/cmd/web/main.go index 093dad6..8456ad8 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -9,14 +9,17 @@ import ( "time" "github.com/alexedwards/scs/v2" + "github.com/casbin/casbin" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "golang.org/x/oauth2" "golang.org/x/oauth2/google" + "git.ofmax.li/iserv/internal/acct" "git.ofmax.li/iserv/internal/auth" "git.ofmax.li/iserv/internal/db/redis" "git.ofmax.li/iserv/internal/fs" + "git.ofmax.li/iserv/internal/goog" "git.ofmax.li/iserv/internal/image" "go.ofmax.li/tmpl" ) @@ -54,6 +57,9 @@ func main() { sessionManager := scs.New() sessionManager.Lifetime = 24 * time.Hour + // Google + googService := goog.NewService(oauthClientConfig) + // Image imgdb := redis.NewRedisImageRepo(connPool) imageService := image.NewService(imgdb, storagePath, renderer) @@ -61,22 +67,32 @@ func main() { imageFile := fs.NewHandler(storagePath) // Auth + // Auth Enforcer + enf := casbin.NewEnforcer("auth_model.ini", "policy.csv") authdb := redis.NewRedisAuthRepo(connPool) - authService := auth.NewService(authdb) - authHandler := auth.NewHandler(authService, sessionManager, oauthClientConfig) + authService := auth.NewService(authdb, googService, enf) + authHandler := auth.NewHandler(authService, sessionManager) + + // Acct + acctDb := redis.NewAcctRepo(connPool) + acctService := acct.NewService(acctDb, authdb, googService) + acctHandler := acct.NewHandler(acctService, sessionManager) // Static Files staticFiles := fs.NewHandler(path.Join(storagePath, "static")) r := chi.NewRouter() + r.Use(middleware.StripSlashes) r.Use(middleware.Logger) r.Use(sessionManager.LoadAndSave) + r.Use(auth.AuthOnly(authService, sessionManager)) r.Get("/a/g", authHandler.OauthCallback) r.Get("/l", authHandler.Login) + r.Get("/u/register", acctHandler.Register) r.Get("/i/{fileName}", imageHandler.GetImage) - r.Post("/u", imageHandler.PostImage) + r.Post("/i", imageHandler.PostImage) r.Get("/f/*", imageFile) r.Get("/static/*", staticFiles) @@ -4,10 +4,15 @@ go 1.14 require ( github.com/alexedwards/scs/v2 v2.4.0 + github.com/apex/log v1.9.0 github.com/brianvoe/gofakeit v3.18.0+incompatible github.com/bwmarrin/snowflake v0.3.0 // indirect + github.com/casbin/casbin v1.9.1 + github.com/casbin/casbin/v2 v2.19.8 // indirect + github.com/casbin/chi-authz v0.0.0-20170726155602-f9f57e3793f2 // indirect github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2 github.com/go-chi/chi v4.1.0+incompatible + github.com/go-delve/delve v1.5.0 // indirect github.com/golang/mock v1.4.4 github.com/gomodule/redigo v2.0.0+incompatible github.com/matoous/go-nanoid v1.3.0 @@ -15,11 +20,12 @@ require ( github.com/pkg/errors v0.9.1 github.com/rafaeljusto/redigomock v2.3.0+incompatible github.com/speps/go-hashids v2.0.0+incompatible // indirect - github.com/stretchr/testify v1.5.1 + github.com/stretchr/testify v1.6.1 gitlab.com/grumps/environ v0.0.0-20190605051324-730aa37373e1 // indirect go.ofmax.li/environ v0.1.0 go.ofmax.li/tmpl v0.1.0 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/tools v0.0.0-20201031021630-582c62ec74d0 // indirect google.golang.org/api v0.21.0 + gopkg.in/yaml.v2 v2.2.2 // indirect ) @@ -4,28 +4,55 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/alexedwards/scs v1.4.1 h1:/5L5a07IlqApODcEfZyMsu8Smd1S7Q4nBjEyKxIRTp0= github.com/alexedwards/scs/v2 v2.3.0 h1:V8rtn2P5QGh8C9S7T/ikBo/AdA27vDoQJPbiAaOCmFg= github.com/alexedwards/scs/v2 v2.3.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= github.com/alexedwards/scs/v2 v2.4.0 h1:XfnMamKnvp1muJVNr1WzikQTclopsBXWZtzz0NBjOK0= github.com/alexedwards/scs/v2 v2.4.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= +github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= +github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= +github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/brianvoe/gofakeit v1.2.0 h1:GGbzCqQx9ync4ObAUhRa3F/M73eL9VZL3X09WoTwphM= github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM= +github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog= +github.com/casbin/casbin/v2 v2.19.8 h1:hmlbARamCCE9hmcE7N/NRQqzti6Wu8CdF+QwpXzhFQU= +github.com/casbin/casbin/v2 v2.19.8/go.mod h1:wUgota0cQbTXE6Vd+KWpg41726jFRi7upxio0sR+Xd0= +github.com/casbin/chi-authz v0.0.0-20170726155602-f9f57e3793f2 h1:lWi4Rzt/ERv26z3UTaf7DEKlGmkYJRYqMHK35k5I80g= +github.com/casbin/chi-authz v0.0.0-20170726155602-f9f57e3793f2/go.mod h1:i93bCyqcO3IKbKqjg/FqtocZt5LOM75rpX/Gj7v8kvo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= +github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gbrlsnchs/jwt v1.1.0 h1:Gh2CoXcIfk8/LxV8ks0GDOmUDCpVIrw8Oa34Ozmw/10= github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2 h1:3t7jvTkeQfk1FdP0noXSNiM6AdBokLz7QmZDmnCHAAA= github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.2/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM= github.com/go-chi/chi v1.0.0 h1:s/kv1cTXfivYjdKJdyUzNGyAWZ/2t7duW1gKn5ivu+c= github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-delve/delve v1.5.0 h1:gQsRvFdR0BGk19NROQZsAv6iG4w5QIZoJlxJeEUBb0c= +github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -34,6 +61,7 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gomodule/redigo v1.7.0 h1:ZKld1VOtsGhAe37E7wMxEDgAlGM5dvFY+DiOhSkhP9Y= @@ -44,21 +72,51 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-dap v0.2.0 h1:whjIGQRumwbR40qRU7CEKuFLmePUUc2s4Nt9DoXXxWk= +github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/matoous/go-nanoid v1.3.0 h1:ynznZVSo9t0E8BTYLZx9geceRYZr8yLIrkOv3C/CU8M= github.com/matoous/go-nanoid v1.3.0/go.mod h1:fvGBnhcQ+zcrB3qJIG32PAN11J/y1IYkGX2/VeHzuH0= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561 h1:isR/L+BIZ+rqODWYR/f526ygrBMGKZYFhaaFRDGvuZ8= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/muyo/sno v1.1.0 h1:WLsHreJnNFtc29Z2grb9Y9A6nXLrDJS0KqpWZzTYGLg= github.com/muyo/sno v1.1.0/go.mod h1:Hjr5WzLleYD7bJYvQGo5UK7hpE2kSvMbihc0bMWF2bU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b h1:8uaXtUkxiy+T/zdLWuxa/PG4so0TPZDZfafFNNSaptE= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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= @@ -66,13 +124,36 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rafaeljusto/redigomock v2.3.0+incompatible h1:mW+5Fc1qpEgyPBIsT1ZdYAqoC/hRgq9bxUGiToBMr6A= github.com/rafaeljusto/redigomock v2.3.0+incompatible/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/speps/go-hashids v1.0.0 h1:jdFC07PrExRM4Og5Ev4411Tox75aFpkC77NlmutadNI= github.com/speps/go-hashids v2.0.0+incompatible h1:kSfxGfESueJKTx0mpER9Y/1XHl+FVQjtCqRyYcviFbw= github.com/speps/go-hashids v2.0.0+incompatible/go.mod h1:P7hqPzMdnZOfyIk+xrlG1QaSMw+gCBdHKsBDnhpaZvc= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372 h1:eRfW1vRS4th8IX2iQeyqQ8cOUNOySvAYJ0IUvTXGoYA= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1 h1:7bozMfSdo41n2NOc0GsVTTVUiA+Ncaj6pXNpm4UHKys= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= gitlab.com/grumps/environ v0.0.0-20190605051324-730aa37373e1 h1:Jjns+kL26DLZhyrxqBZHJn1ymX33jtfnmv2xufSXOaA= gitlab.com/grumps/environ v0.0.0-20190605051324-730aa37373e1/go.mod h1:GPGILReyQ0kLhYL5BcbmvTUGCmHC+upQgUZBQMqvcDY= @@ -82,7 +163,12 @@ go.ofmax.li/tmpl v0.1.0 h1:itGhtIes5ba/s/0x0i9x+e/6my6MDTkd9KUhwRr/cVc= go.ofmax.li/tmpl v0.1.0/go.mod h1:gzv8lp+KPxqP6f7FqoBdGl8UwXANmuLOXxyOaKNUaQg= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.starlark.net v0.0.0-20190702223751-32f345186213 h1:lkYv5AKwvvduv5XWP6szk/bvvgO6aDeUujhZQXIFTes= +go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -98,6 +184,7 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -119,10 +206,14 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= @@ -143,6 +234,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e h1:1xWUkZQQ9Z9UuZgNaIR6OQOE7rUFglXUUBZlO+dGg6I= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201031021630-582c62ec74d0 h1:obBdJPIfkOi5/rVh102giHaq0G8BZGE4eGB+NU6SgBo= golang.org/x/tools v0.0.0-20201031021630-582c62ec74d0/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -169,8 +261,16 @@ google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/acct/acct.go b/internal/acct/acct.go new file mode 100644 index 0000000..48a71e6 --- /dev/null +++ b/internal/acct/acct.go @@ -0,0 +1,106 @@ +package acct + +import ( + "context" + "net/http" + + "git.ofmax.li/iserv/internal/auth" + "git.ofmax.li/iserv/internal/goog" + "github.com/alexedwards/scs/v2" + "golang.org/x/oauth2" +) + +// handler + +// Handler interface to http handler +type Handler interface { + Register(w http.ResponseWriter, r *http.Request) +} + +type acctHandler struct { + svc Servicer + ses *scs.SessionManager +} + +func (h *acctHandler) Register(w http.ResponseWriter, r *http.Request) { + userID := h.ses.GetString(r.Context(), "profid") + if userID == "" { + http.Redirect(w, r, "/l", http.StatusFound) + } + h.svc.GetGoogProfile(r.Context(), userID) + return +} + +// NewHandler create new instance of handler +// Servicer, session, and oauth client required +func NewHandler(service Servicer, session *scs.SessionManager) Handler { + return &acctHandler{ + service, + session, + } +} + +// endhandler + +// model + +// Profile application account profile +type Profile interface { + ProfileKeyValues() (string, []string) +} + +// endmodel + +// repo +// Repo storage interface + +type Repo interface { + UpdateAcctProfile(p Profile) error +} + +// endrepo + +// service + +type Servicer interface { + UpdateProfile(p *Profile) error + GetGoogProfile(ctx context.Context, id string) error +} + +func NewService(repo Repo, authRepo auth.Repo, goog goog.Servicer) *service { + return &service{ + repo, + authRepo, + goog, + } +} + +type service struct { + repo Repo + authRepo auth.Repo + goog goog.Servicer +} + +func (s *service) UpdateProfile(p *Profile) error { + return nil +} + +func (s *service) GetGoogProfile(ctx context.Context, id string) error { + token, err := s.authRepo.GetProfileToken(id) + if err != nil { + return err + } + client := s.goog.UserClient(ctx, token) + gp, _, err := s.goog.Profile(client) + if err != nil { + return err + } + s.repo.UpdateAcctProfile(gp) + return nil +} + +func (s *service) GetProfileToken(id string) (*oauth2.Token, error) { + return s.authRepo.GetProfileToken(id) +} + +// endservice diff --git a/internal/auth/handler.go b/internal/auth/handler.go index 992608c..d57d47e 100644 --- a/internal/auth/handler.go +++ b/internal/auth/handler.go @@ -3,15 +3,12 @@ package auth import ( "encoding/json" "io/ioutil" - "log" "net/http" "github.com/alexedwards/scs/v2" "golang.org/x/oauth2" -) -var ( - profileURL = "https://www.googleapis.com/oauth2/v3/userinfo" + "git.ofmax.li/iserv/internal/goog" ) // Handler authentication handler @@ -22,30 +19,28 @@ type Handler interface { } type authHandler struct { - service Servicer - ses *scs.SessionManager - oclient *oauth2.Config + svc Servicer + ses *scs.SessionManager } +// TODO migrate to Goog // NewHandler create new instance of handler // Servicer, session, and oauth client required func NewHandler(service Servicer, - session *scs.SessionManager, - oclient *oauth2.Config) Handler { + session *scs.SessionManager) Handler { return &authHandler{ service, session, - oclient, } } func (h *authHandler) Login(w http.ResponseWriter, r *http.Request) { - stateValue, err := h.service.GenerateStateToken() + stateValue, err := h.svc.GenerateStateToken() if err != nil { return } h.ses.Put(r.Context(), "state", stateValue) - url := h.oclient.AuthCodeURL(stateValue, oauth2.AccessTypeOnline) + url := h.svc.Goog().Config().AuthCodeURL(stateValue, oauth2.AccessTypeOnline) http.Redirect(w, r, url, 302) } @@ -58,7 +53,7 @@ func (h *authHandler) OauthCallback(w http.ResponseWriter, r *http.Request) { http.Error(w, "state value miss match bad data", 400) return } - stateValid, err := h.service.ValidateStateToken(stateFromCallback, stateFromSession) + stateValid, err := h.svc.ValidateStateToken(stateFromCallback, stateFromSession) if err != nil { http.Error(w, "error validating", 400) return @@ -69,15 +64,14 @@ func (h *authHandler) OauthCallback(w http.ResponseWriter, r *http.Request) { } // valid and same as state code := r.FormValue("code") - token, err := h.oclient.Exchange(r.Context(), code) + token, err := h.svc.Goog().Config().Exchange(r.Context(), code) if err != nil { http.Error(w, err.Error(), 400) return } - log.Printf("returned token %v", token) // google profile - client := h.oclient.Client(r.Context(), token) - resp, err := client.Get(profileURL) + client := h.svc.Goog().UserClient(r.Context(), token) + resp, err := client.Get(goog.ProfileURL) if err != nil { http.Error(w, err.Error(), 400) return @@ -88,9 +82,9 @@ func (h *authHandler) OauthCallback(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), 400) return } - gp := &GoogleAuthProfile{} + gp := &goog.GoogleProfile{} err = json.Unmarshal(data, &gp) - profileID, newProfile, err := h.service.LoginOrRegisterSessionID(token, gp) + profileID, newProfile, err := h.svc.LoginOrRegisterSessionID(token, gp) if err != nil { http.Error(w, err.Error(), 400) return @@ -98,8 +92,9 @@ func (h *authHandler) OauthCallback(w http.ResponseWriter, r *http.Request) { h.ses.Put(r.Context(), "profid", profileID) // send to registration if newProfile == true { - http.Redirect(w, r, "/account/register", 302) + http.Redirect(w, r, "/u/register", 302) return } http.Redirect(w, r, "/", 302) + return } diff --git a/internal/auth/middleware.go b/internal/auth/middleware.go new file mode 100644 index 0000000..0be033c --- /dev/null +++ b/internal/auth/middleware.go @@ -0,0 +1,45 @@ +package auth + +import ( + "net/http" + + "github.com/alexedwards/scs/v2" + "github.com/apex/log" +) + +const ( + loginURL = "/login" +) + +func AuthOnly(s Servicer, ses *scs.SessionManager) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + userID := ses.GetString(r.Context(), "profid") + if userID == "" { + userID = "anon" + } + resource := r.URL.Path + // set the action to something that will never match + action := "forbidden" + switch r.Method { + case "POST", "PUT", "PATCH": + action = "write" + case "HEAD", "GET": + action = "read" + } + // TODO determine action + enforced, err := s.Enf().EnforceSafe(userID, resource, action) + if err != nil { + log.Errorf("%s", err) + return + } + if !enforced { + // TODO probably need to do something about suggesting to login + http.Error(w, "not found, are you signed in?", http.StatusNotFound) + return + } + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + } +} diff --git a/internal/auth/model.go b/internal/auth/model.go index c51ff05..240b11b 100644 --- a/internal/auth/model.go +++ b/internal/auth/model.go @@ -6,20 +6,10 @@ import ( "golang.org/x/oauth2" ) -// GoogleAuthProfile auth'd user profile -// ProfileId, Email, Name, PictureURL -type GoogleAuthProfile struct { - ProfileID string `json:"sub"` - Email string `json:"email"` - Name string `json:"name"` - PictureURL string `json:"picture"` -} - // Profile profile and token // Identifier + Token type Profile struct { ID string `json:"sub"` - Email string `json:"email"` AccessToken string `json:"access_token"` TokenType string `json:"token_type,omitempty"` RefreshToken string `json:"refresh_token,omitempty"` @@ -37,10 +27,9 @@ func (ap *Profile) Token() (*oauth2.Token, error) { } // NewAuthProfile merge token and profile -func NewAuthProfile(t *oauth2.Token, u *GoogleAuthProfile) *Profile { +func NewAuthProfile(t *oauth2.Token, id string) *Profile { return &Profile{ - u.ProfileID, - u.Email, + id, t.AccessToken, t.TokenType, t.RefreshToken, diff --git a/internal/auth/repo.go b/internal/auth/repo.go index f404c94..9a0420e 100644 --- a/internal/auth/repo.go +++ b/internal/auth/repo.go @@ -1,8 +1,15 @@ package auth +import ( + "git.ofmax.li/iserv/internal/goog" + "golang.org/x/oauth2" +) + // Repo storage interface type Repo interface { - IsAuthorized(gp *GoogleAuthProfile) (bool, error) - LookUpAuthProfileID(gp *GoogleAuthProfile) (string, error) - SaveAuthProfile(ap *Profile) error + IsAuthorized(gp *goog.GoogleProfile) (bool, error) + LookUpAuthProfileID(gp *goog.GoogleProfile) (string, error) + SaveAuthProfile(email string, ap *Profile) error + CheckProfileID(id string) (bool, error) + GetProfileToken(id string) (*oauth2.Token, error) } diff --git a/internal/auth/service.go b/internal/auth/service.go index 9997264..e85c705 100644 --- a/internal/auth/service.go +++ b/internal/auth/service.go @@ -2,11 +2,14 @@ package auth import ( "errors" + "fmt" "log" "time" + "git.ofmax.li/iserv/internal/goog" "golang.org/x/oauth2" + "github.com/casbin/casbin" "github.com/gbrlsnchs/jwt/v3" ) @@ -22,23 +25,40 @@ var ( // Servicer access to auth functionality type Servicer interface { - LoginOrRegisterSessionID(t *oauth2.Token, gp *GoogleAuthProfile) (string, bool, error) + Goog() goog.Servicer + LoginOrRegisterSessionID(t *oauth2.Token, gp *goog.GoogleProfile) (string, bool, error) GenerateStateToken() (string, error) ValidateStateToken(token string, sessionToken string) (bool, error) + CheckProfileID(id string) (bool, error) + Enf() *casbin.Enforcer } // Service a container for auth deps type Service struct { repo Repo + goog goog.Servicer + enf *casbin.Enforcer } // NewService create auth service -func NewService(repo Repo) *Service { +func NewService(repo Repo, goog goog.Servicer, enf *casbin.Enforcer) *Service { return &Service{ repo, + goog, + enf, } } +// Goog get google interface +func (a *Service) Goog() goog.Servicer { + return a.goog +} + +// Enf enforcer instance +func (a *Service) Enf() *casbin.Enforcer { + return a.enf +} + // GenerateStateToken create a random token for oauth exchange func (a *Service) GenerateStateToken() (string, error) { now := time.Now() @@ -67,8 +87,13 @@ func (a *Service) ValidateStateToken(token string, sessionToken string) (bool, e return false, ErrInvalidToken } +// CheckProfileID check if a profileid exists +func (a *Service) CheckProfileID(id string) (bool, error) { + return a.repo.CheckProfileID(id) +} + // LoginOrRegisterSessionID create a login -func (a *Service) LoginOrRegisterSessionID(t *oauth2.Token, gp *GoogleAuthProfile) (string, bool, error) { +func (a *Service) LoginOrRegisterSessionID(t *oauth2.Token, gp *goog.GoogleProfile) (string, bool, error) { isAuthorized, err := a.repo.IsAuthorized(gp) newRegistration := false if err != nil { @@ -84,10 +109,9 @@ func (a *Service) LoginOrRegisterSessionID(t *oauth2.Token, gp *GoogleAuthProfil if profileID == "" { // create profile log.Printf("creating new profile") - profile := NewAuthProfile(t, gp) + profile := NewAuthProfile(t, fmt.Sprintf("goog:%s", gp.ProfileID)) profileID = profile.ID - log.Printf("new profile %+v", profile) - err = a.repo.SaveAuthProfile(profile) + err = a.repo.SaveAuthProfile(gp.Email, profile) if err != nil { return "", newRegistration, err } diff --git a/internal/auth/service_test.go b/internal/auth/service_test.go index 72ff709..b992696 100644 --- a/internal/auth/service_test.go +++ b/internal/auth/service_test.go @@ -11,6 +11,7 @@ import ( "github.com/golang/mock/gomock" "git.ofmax.li/iserv/internal/auth" + "git.ofmax.li/iserv/internal/goog" "git.ofmax.li/iserv/internal/mock/mock_auth" ) @@ -77,7 +78,7 @@ func (s *serviceSuite) testValidateStateToken() func(t *testing.T) { func (s *serviceSuite) testLoginOrRegsiterSessionId() func(t *testing.T) { return func(t *testing.T) { // is authorized err - gp := &auth.GoogleAuthProfile{} + gp := &goog.GoogleProfile{} token := &oauth2.Token{} gofakeit.Struct(token) gofakeit.Struct(gp) diff --git a/internal/db/redis/acct.go b/internal/db/redis/acct.go new file mode 100644 index 0000000..72df741 --- /dev/null +++ b/internal/db/redis/acct.go @@ -0,0 +1,44 @@ +package redis + +import ( + "fmt" + + "github.com/gomodule/redigo/redis" + "github.com/pkg/errors" + + "git.ofmax.li/iserv/internal/acct" +) + +// AcctRepo account +type AcctRepo struct { + db *redis.Pool +} + +var ( + acctProfileKey string = "acct:email:%s" +) + +// NewAcctRepo account repo +func NewAcctRepo(conn *redis.Pool) *AcctRepo { + return &AcctRepo{ + conn, + } +} + +// UpdateAcctProfile write profile to redis +func (db *AcctRepo) UpdateAcctProfile(p acct.Profile) error { + conn := db.db.Get() + defer conn.Close() + profileID, profileKeyValues := p.ProfileKeyValues() + acctProfileKey := fmt.Sprintf(acctProfileKey, profileID) + profileArgs := redis.Args{}.Add(acctProfileKey).AddFlat(profileKeyValues) + res, err := redis.String(conn.Do("HMSET", profileArgs...)) + if err != nil { + fmt.Print(err) + return err + } + if res != "OK" { + return errors.Errorf("%s acct was not saved", acctProfileKey) + } + return nil +} diff --git a/internal/db/redis/auth.go b/internal/db/redis/auth.go index 0c5246c..1f99fac 100644 --- a/internal/db/redis/auth.go +++ b/internal/db/redis/auth.go @@ -1,12 +1,14 @@ package redis import ( + "fmt" "log" "github.com/gomodule/redigo/redis" "golang.org/x/oauth2" "git.ofmax.li/iserv/internal/auth" + "git.ofmax.li/iserv/internal/goog" ) var ( @@ -16,6 +18,14 @@ var ( end return nil `) + checkActiveAuthLua = redis.NewScript(1, ` + local email = redis.call("HMGET",KEYS[1],"email") + if email == nil then + return false + end + return redis.call("SISMEMBER",email) + `) + authProfileKey = func(id string) string { return fmt.Sprintf("auth:%s") } ) // AuthRepo supporting auth @@ -32,11 +42,11 @@ func NewRedisAuthRepo(conn *redis.Pool) *AuthRepo { } // IsAuthorized is member of invites -func (db *AuthRepo) IsAuthorized(gp *auth.GoogleAuthProfile) (bool, error) { +func (db *AuthRepo) IsAuthorized(gp *goog.GoogleProfile) (bool, error) { conn := db.db.Get() defer conn.Close() - authProfileKey := "auth:goog:" + gp.Email - reply, err := redis.Bool(conn.Do("SISMEMBER", "invites", authProfileKey)) + inviteKey := "goog:" + gp.Email + reply, err := redis.Bool(conn.Do("SISMEMBER", "invites", inviteKey)) if err != nil { return reply, err } @@ -44,16 +54,12 @@ func (db *AuthRepo) IsAuthorized(gp *auth.GoogleAuthProfile) (bool, error) { } // LookUpAuthProfileID get internal profileid -func (db *AuthRepo) LookUpAuthProfileID(gp *auth.GoogleAuthProfile) (string, error) { +func (db *AuthRepo) LookUpAuthProfileID(gp *goog.GoogleProfile) (string, error) { conn := db.db.Get() var id string defer conn.Close() - authProfileKey := "auth:goog:" + gp.ProfileID - reply, err := redis.Values(conn.Do("HMGET", authProfileKey, "id")) - if err == redis.ErrNil { - // no profile - return "", nil - } else if err != nil { + reply, err := redis.Values(conn.Do("HMGET", authProfileKey(gp.ProfileID), "id")) + if err != nil { // some other error return "", err } @@ -64,24 +70,35 @@ func (db *AuthRepo) LookUpAuthProfileID(gp *auth.GoogleAuthProfile) (string, err } // SaveAuthProfile save profile, validate against invites -func (db *AuthRepo) SaveAuthProfile(ap *auth.Profile) error { +func (db *AuthRepo) SaveAuthProfile(email string, ap *auth.Profile) error { // TODO this will over-write refresh tokens conn := db.db.Get() defer conn.Close() - authProfileKey := "auth:goog:" + ap.ID - prof := redis.Args{}.Add(ap.Email).Add(authProfileKey).AddFlat(ap) - res, err := createAuthLua.Do(conn, prof...) - log.Printf("auth save response %+v", res) + prof := redis.Args{}.Add(email).Add(ap.ID).AddFlat(ap) + _, err := createAuthLua.Do(conn, prof...) conn.Flush() return err } +// CheckProfileID return true if the profile exists and authorized +func (db *AuthRepo) CheckProfileID(id string) (bool, error) { + conn := db.db.Get() + defer conn.Close() + res, err := redis.Int(checkActiveAuthLua.Do(conn, authProfileKey(id))) + if err == redis.ErrNil { + return false, nil + } else if err != nil { + return false, err + } + // there could be profile created but later revoked authorization + return res == 1, nil +} + func (db *AuthRepo) getAuthProfile(id string) (*auth.Profile, error) { conn := db.db.Get() defer conn.Close() - authProfileKey := "auth:goog:" + id authProfile := auth.Profile{} - res, err := redis.Values(conn.Do("HGETALL", authProfileKey)) + res, err := redis.Values(conn.Do("HGETALL", authProfileKey(id))) if err != nil { log.Fatal(err) } diff --git a/internal/db/redis/image.go b/internal/db/redis/image.go index f216531..3622728 100644 --- a/internal/db/redis/image.go +++ b/internal/db/redis/image.go @@ -48,7 +48,7 @@ func (r *ImageRepo) GetFile(fileUrl string) (*image.PostMeta, error) { key := fileKey(fileUrl, V1FilePathFmt) res, err := redis.Values(conn.Do("HGETALL", key)) if err != nil { - return &image.PostMeta{}, ErrNotFound + return &image.PostMeta{}, err } err = redis.ScanStruct(res, imageMeta) if imageMeta.FilePath == "" { diff --git a/internal/goog/goog.go b/internal/goog/goog.go new file mode 100644 index 0000000..5102a95 --- /dev/null +++ b/internal/goog/goog.go @@ -0,0 +1,89 @@ +package goog + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "golang.org/x/oauth2" +) + +var ( + ProfileURL = "https://www.googleapis.com/oauth2/v3/userinfo" + ProfileKeyFmt = "goog:%s" +) + +// GoogleProfile auth'd user profile +// ProfileId, Email, Name, PictureURL +type GoogleProfile struct { + ProfileID string `json:"sub"` + Email string `json:"email"` + Name string `json:"name"` + PictureURL string `json:"picture"` +} + +// ProfileKeys conform to ProfileKeys +func (gp *GoogleProfile) ProfileKeyValues() (string, []string) { + return fmt.Sprintf(ProfileKeyFmt, gp.Email), []string{ + "profile_id", gp.ProfileID, + "email", gp.Email, + "name", gp.Name, + "picture_url", gp.PictureURL, + } +} + +type Servicer interface { + Config() *oauth2.Config + Profile(client *http.Client) (*GoogleProfile, *oauth2.Token, error) + UserClient(ctx context.Context, token *oauth2.Token) *http.Client +} + +// NewService container for interacting with Google +func NewService(o *oauth2.Config) Servicer { + return &Goog{ + o, + } + +} + +type Goog struct { + oauthConfig *oauth2.Config +} + +func (g *Goog) Config() *oauth2.Config { + return g.oauthConfig +} + +// UserCient just calls oauth2.Client +func (g *Goog) UserClient(ctx context.Context, token *oauth2.Token) *http.Client { + return g.oauthConfig.Client(ctx, token) +} + +// Profile get profile from google +func (g *Goog) Profile(client *http.Client) (*GoogleProfile, *oauth2.Token, error) { + client.Get(ProfileURL) + resp, err := client.Get(ProfileURL) + if err != nil { + return &GoogleProfile{}, &oauth2.Token{}, err + } + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return &GoogleProfile{}, &oauth2.Token{}, err + } + // Google Profile + gp := &GoogleProfile{} + err = json.Unmarshal(data, gp) + if err != nil { + return &GoogleProfile{}, &oauth2.Token{}, err + } + // Token + token := &oauth2.Token{} + err = json.Unmarshal(data, token) + if err != nil { + return &GoogleProfile{}, &oauth2.Token{}, err + } + return gp, token, nil +} diff --git a/internal/image/handler.go b/internal/image/handler.go index f41ed4d..71a5383 100644 --- a/internal/image/handler.go +++ b/internal/image/handler.go @@ -35,9 +35,8 @@ func (h *imageHandler) GetImage(w http.ResponseWriter, r *http.Request) { fileID := chi.URLParam(r, "fileName") fileMeta, err := h.service.GetFile(fileID) if err != nil { - w.WriteHeader(400) - log.Printf("error: %+v", err) - w.Write([]byte("WTF Incorrect Content Type")) + http.Error(w, "an error has occured", http.StatusBadRequest) + return } fileUrl := fmt.Sprintf("/f/%s", fileMeta.FilePath) data := struct { diff --git a/internal/image/service.go b/internal/image/service.go index 10f4148..2a60c67 100644 --- a/internal/image/service.go +++ b/internal/image/service.go @@ -78,6 +78,7 @@ func (is *Service) AddFile(extension string, postMeta *PostMeta, fileBytes []byt func (is *Service) GetFile(fileUrl string) (*PostMeta, error) { result, err := is.db.GetFile(fileUrl) if err != nil { + return &PostMeta{}, err } return result, err } diff --git a/policy.csv b/policy.csv new file mode 100644 index 0000000..1784828 --- /dev/null +++ b/policy.csv @@ -0,0 +1,6 @@ +p, goog:113063035911692090595, /i/dixiesdfsf.jpg, write +p, goog:113063035911692090595, /u/register, read +p, anon, /l, read|write +p, anon, /a/g, read|write +g, goog:113063035911692090595, member +g2, member , /, read |