From f326e994dac09a803e8602ea1bfeb11e28b765a0 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Tue, 18 Jun 2019 18:33:38 +0200 Subject: [PATCH] Import sources from Kontor --- cmd/root.go | 6 +- go.mod | 47 +++++++ go.sum | 286 +++++++++++++++++++++++++++++++++++++++ pkg/admin/routes.go | 23 ++++ pkg/admin/user.go | 18 +++ pkg/admin/user_dao.go | 156 +++++++++++++++++++++ pkg/admin/user_test.go | 105 ++++++++++++++ pkg/admin/views.go | 128 ++++++++++++++++++ pkg/auth/middleware.go | 85 ++++++++++++ pkg/auth/session.go | 11 ++ pkg/auth/session_dao.go | 78 +++++++++++ pkg/auth/session_test.go | 103 ++++++++++++++ pkg/dao/database.go | 31 +++++ pkg/dao/database_test.go | 50 +++++++ pkg/properties/root.go | 25 ++++ pkg/setup/data.go | 11 ++ pkg/setup/routes.go | 28 ++++ pkg/setup/session.go | 15 ++ pkg/setup/user.go | 20 +++ pkg/util/render.go | 39 ++++++ 20 files changed, 1262 insertions(+), 3 deletions(-) create mode 100644 go.sum create mode 100644 pkg/admin/routes.go create mode 100644 pkg/admin/user.go create mode 100644 pkg/admin/user_dao.go create mode 100644 pkg/admin/user_test.go create mode 100644 pkg/admin/views.go create mode 100644 pkg/auth/middleware.go create mode 100644 pkg/auth/session.go create mode 100644 pkg/auth/session_dao.go create mode 100644 pkg/auth/session_test.go create mode 100644 pkg/dao/database.go create mode 100644 pkg/dao/database_test.go create mode 100644 pkg/properties/root.go create mode 100644 pkg/setup/data.go create mode 100644 pkg/setup/routes.go create mode 100644 pkg/setup/session.go create mode 100644 pkg/setup/user.go create mode 100644 pkg/util/render.go diff --git a/cmd/root.go b/cmd/root.go index 6efe149..502b52b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,9 +27,9 @@ var ( ) var rootCmd = &cobra.Command{ - Use: "kontor", - Short: "kontor", - Long: `kontor`, + Use: "kalorienmanager", + Short: "kalorienmanager", + Long: `kalorienmanager`, Run: func(cmd *cobra.Command, args []string) { // Set Gin to production mode if Debug { diff --git a/go.mod b/go.mod index 7e7177b..57c35cf 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,48 @@ module gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git + +require ( + cloud.google.com/go v0.40.0 // indirect + github.com/coreos/bbolt v1.3.3 // indirect + github.com/coreos/etcd v3.3.13+incompatible // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd v0.0.0-20190617083831-1652836e9bdc // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.4.0 + github.com/golang/mock v1.3.1 // indirect + github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.9.2 // indirect + github.com/kisielk/errcheck v1.2.0 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/kr/pty v1.1.5 // indirect + github.com/magiconair/properties v1.8.1 // indirect + github.com/mattn/go-isatty v0.0.8 // indirect + github.com/pelletier/go-toml v1.4.0 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/prometheus/common v0.5.0 // indirect + github.com/rogpeppe/fastuuid v1.1.0 // indirect + github.com/russross/blackfriday v2.0.0+incompatible // indirect + github.com/sirupsen/logrus v1.4.2 // indirect + github.com/spf13/afero v1.2.2 // indirect + github.com/spf13/cobra v0.0.5 + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/viper v1.4.0 // indirect + github.com/stretchr/objx v0.2.0 // indirect + github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect + github.com/tebeka/go2xunit v1.4.10 // indirect + github.com/ugorji/go v1.1.5-pre // indirect + go.etcd.io/bbolt v1.3.3 // indirect + go.opencensus.io v0.22.0 // indirect + golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56 + golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect + golang.org/x/image v0.0.0-20190616094056-33659d3de4f5 // indirect + golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88 // indirect + golang.org/x/mod v0.1.0 // indirect + golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f // indirect + golang.org/x/tools v0.0.0-20190617190820-da514acc4774 // indirect + google.golang.org/appengine v1.6.1 // indirect + google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 // indirect + google.golang.org/grpc v1.21.1 // indirect + gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce + honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b3d0655 --- /dev/null +++ b/go.sum @@ -0,0 +1,286 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190617083831-1652836e9bdc/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +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/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.5.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.8.0/go.mod h1:fSI0j+IUQrDd7+ZtR9WKIGtoYAYAJUKcKhYLG25tN4g= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8= +github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk= +github.com/tebeka/go2xunit v1.4.10 h1:0UO+9YoLpXTZ0DL9XbTmIIibgmKBGiwroo8uhFMSyR0= +github.com/tebeka/go2xunit v1.4.10/go.mod h1:wmc9jKT7KlU4QLU6DNTaIXNnYNOjKKNlp6mjOS0UrqY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.5-pre h1:jyJKFOSEbdOc2HODrf2qcCkYOdq7zzXqA9bhW5oV4fM= +github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.5-pre h1:5YV9PsFAN+ndcCtTM7s60no7nY7eTG3LPtxhSwuxzCs= +github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56 h1:ZpKuNIejY8P0ExLOVyKhb0WsgG8UdvHXe6TWjY7eL6k= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190616094056-33659d3de4f5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +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= +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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/admin/routes.go b/pkg/admin/routes.go new file mode 100644 index 0000000..82de9dc --- /dev/null +++ b/pkg/admin/routes.go @@ -0,0 +1,23 @@ +package admin + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/util" + + "github.com/gin-gonic/gin" +) + +// GetRoutes sets the routes for the administrative data urls. +func GetRoutes(router *gin.Engine) { + adminRoutes := router.Group("/admin") + { + adminRoutes.GET("/", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showAdminIndex) + adminRoutes.GET("/user", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserIndex) + adminRoutes.POST("/user", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserIndex) + adminRoutes.GET("/user/view/:userid", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserDetails) + adminRoutes.POST("/user/view", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), validateUserCreation) + adminRoutes.GET("/user/create", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), showUserCreation) + adminRoutes.POST("/user/create", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), validateUserCreation) + adminRoutes.GET("/data", auth.EnsureLoggedIn(), auth.EnsureAdminStatus(), util.ShowIndexPage) + } +} diff --git a/pkg/admin/user.go b/pkg/admin/user.go new file mode 100644 index 0000000..8d94a06 --- /dev/null +++ b/pkg/admin/user.go @@ -0,0 +1,18 @@ +package admin + +import ( + "gopkg.in/mgo.v2/bson" +) + +// User defines the data model for application users with id,email, user name, +// first and family name, password and admin status. +type User struct { + ID bson.ObjectId `json:"_id" bson:"_id,omitempty"` + Email string `json:"email" bson:"email,omitempty"` + Username string `json:"username" bson:"username,omitempty"` + Firstname string `json:"firstname" bson:"firstname,omitempty"` + Lastname string `json:"lastname" bson:"lastname,omitempty"` + Password string `json:"password" bson:"password,omitempty"` + IsAdmin bool `json:"is_admin" bson:"is_admin,omitempty"` + Model string `json:"model" bson:"model,omitempty"` +} diff --git a/pkg/admin/user_dao.go b/pkg/admin/user_dao.go new file mode 100644 index 0000000..8639652 --- /dev/null +++ b/pkg/admin/user_dao.go @@ -0,0 +1,156 @@ +package admin + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + "log" + + "github.com/gin-gonic/gin" + + "golang.org/x/crypto/bcrypt" + mgo "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" +) + +// UserDAO extends the type BaseDAO. +type UserDAO struct { + Db dao.BaseDAO +} + +const ( + // USERCOLLECTION defines the collection name for storing application user data. + USERCOLLECTION = "user" + // USERMODEL defines the name of the user data model. + USERMODEL = "kalorienmanager.admin.user" +) + +// HashPassword returns the encrypted password from password string. +func HashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) + return string(bytes), err +} + +// CheckPasswordHash returns if password correlates with pasword hash. +func CheckPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +// FindAll retrieves the list of users from the database. +func (m *UserDAO) FindAll() ([]User, error) { + m.Db.Connect() + var users []User + err := m.Db.MongoDb.C(USERCOLLECTION).Find(bson.M{"model": USERMODEL}).All(&users) + return users, err +} + +// FindByID returns a user with given id or returns the error. +func (m *UserDAO) FindByID(id string) (User, error) { + m.Db.Connect() + var user User + err := m.Db.MongoDb.C(USERCOLLECTION).FindId(bson.ObjectIdHex(id)).One(&user) + return user, err +} + +// FindByUsername returns a user with given name or returns the error. +func (m *UserDAO) FindByUsername(username string) (User, error) { + m.Db.Connect() + var user User + err := m.Db.MongoDb.C(USERCOLLECTION).Find(bson.M{"username": username, "model": USERMODEL}).One(&user) + return user, err +} + +// Insert a user into database. +func (m *UserDAO) Insert(user User) error { + m.Db.Connect() + user.Model = USERMODEL + err := m.Db.MongoDb.C(USERCOLLECTION).Insert(&user) + return err +} + +// Upsert a user into database. +func (m *UserDAO) Upsert(user User) (*mgo.ChangeInfo, error) { + m.Db.Connect() + user.Model = USERMODEL + info, err := m.Db.MongoDb.C(USERCOLLECTION).Upsert(bson.M{"username": user.Username}, &user) + return info, err +} + +// Update an existing user. +func (m *UserDAO) Update(user User) error { + m.Db.Connect() + err := m.Db.MongoDb.C(USERCOLLECTION).UpdateId(user.ID, &user) + return err +} + +// Delete an existing user. +func (m *UserDAO) Delete(user User) error { + m.Db.Connect() + err := m.Db.MongoDb.C(USERCOLLECTION).Remove(&user) + return err +} + +// IsUserValid checks if the username and password combination is valid +func (m *UserDAO) IsUserValid(username, password string) bool { + if gin.IsDebugging() { + log.Printf("UserDAO.IsUserValid(%s)", username) + } + user, err := m.FindByUsername(username) + if gin.IsDebugging() { + log.Printf("UserDAO.IsUserValid: %v, %v", user, err) + } + if &user == nil || err != nil { + return false + } + return CheckPasswordHash(password, user.Password) +} + +// IsUserAdmin checks if user identified by name has admin rights. +func (m *UserDAO) IsUserAdmin(username string) bool { + user, err := m.FindByUsername(username) + if &user == nil || err != nil { + return false + } + return user.IsAdmin +} + +// IsUsernameAvailable checks if the supplied username is available. +func (m *UserDAO) IsUsernameAvailable(username string) bool { + user, err := m.FindByUsername(username) + if &user == nil || err != nil { + return true + } + return false +} + +// adduser adds user in database. TODO is method necessary? +func addUser(username, password, firstname, lastname string, isAdmin bool) (*User, error) { + // passwordHash, _ := HashPassword(password) + // var user = User{Username: username, Password: passwordHash, Firstname: firstname, Lastname: lastname, IsAdmin: isAdmin, Model: USERMODEL} + // conn, err := util.GetCollection(USERCOLLECTION) + // err = conn.Insert(&user) + // if err != nil { + // return nil, err + // } + // return &user, nil + return nil, nil +} + +// changeuser changes user in database. TODO is method necessary? +func changeUser(userid, username, password, firstname, lastname string, isAdmin bool) (*User, error) { + // conn, err := util.GetCollection(USERCOLLECTION) + // var user *User + // err = conn.Find(bson.M{"_id": bson.ObjectIdHex(userid)}).One(&user) + // if err != nil { + // return nil, err + // } + // var change bson.M + // if password != "" { + // passwordHash, _ := HashPassword(password) + // change = bson.M{"$set": bson.M{"username": username, "password": passwordHash, "firstname": firstname, "lastname": lastname, "is_admin": isAdmin}} + // } else { + // change = bson.M{"$set": bson.M{"username": username, "firstname": firstname, "lastname": lastname, "is_admin": isAdmin}} + // } + // err = conn.Update(bson.M{"_id": bson.ObjectIdHex(userid)}, change) + // return user, nil + return nil, nil +} diff --git a/pkg/admin/user_test.go b/pkg/admin/user_test.go new file mode 100644 index 0000000..982a333 --- /dev/null +++ b/pkg/admin/user_test.go @@ -0,0 +1,105 @@ +package admin + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + "reflect" + "testing" + + "gopkg.in/mgo.v2/bson" +) + +var userModelTestTable = []struct { + name string + typeName string +}{ + {"Id", "string"}, + {"Email", "string"}, + {"Username", "string"}, + {"Firstname", "string"}, + {"Lastname", "string"}, + {"Password", "string"}, + {"IsAdmin", "bool"}, + {"Model", "string"}, +} + +func TestUserModel(t *testing.T) { + m := User{} + if reflect.TypeOf(m).NumField() != len(userModelTestTable) { + t.Fail() + } + for index, testData := range userModelTestTable { + givenType := reflect.TypeOf(m).Field(index).Type.Kind().String() + if givenType != testData.typeName { + t.Fail() + } + } +} + +func TestListUsers(t *testing.T) { + var userDao = UserDAO{Db: dao.TestDb} + users, err := userDao.FindAll() + if err != nil { + t.Fail() + } + if users != nil { + t.Fail() + } +} + +func TestInsertUser(t *testing.T) { + var ( + userDao = UserDAO{Db: dao.TestDb} + user = User{} + users []User + ) + user.ID = bson.NewObjectId() + user.Username = "test" + err := userDao.Insert(user) + if err != nil { + t.Fail() + } + users, err = userDao.FindAll() + if err != nil { + t.Fail() + } + if len(users) != 1 { + t.Fail() + } +} + +func TestUpsertUser(t *testing.T) { + var ( + userDao = UserDAO{Db: dao.TestDb} + user = User{} + ) + user.ID = bson.NewObjectId() + user.Username = "test2" + userDao.Upsert(user) + users, err := userDao.FindAll() + if err != nil { + t.Fail() + } + if len(users) != 2 { + t.Fail() + } +} + +func TestDeleteUser(t *testing.T) { + var ( + userDao = UserDAO{Db: dao.TestDb} + ) + users, err := userDao.FindAll() + if err != nil { + t.Fail() + } + for _, user := range users { + userDao.Delete(user) + } + users, err = userDao.FindAll() + if err != nil { + t.Fail() + } + if len(users) != 0 { + t.Fail() + } +} diff --git a/pkg/admin/views.go b/pkg/admin/views.go new file mode 100644 index 0000000..187de39 --- /dev/null +++ b/pkg/admin/views.go @@ -0,0 +1,128 @@ +package admin + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/util" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" +) + +// ShowLoginPage renders login page. +func ShowLoginPage(c *gin.Context) { + // Call the render function with the name of the template to render + util.Render(c, gin.H{"title": "Login"}, "login.html") +} + +// PerformLogin reads data from login form and validates input. +func PerformLogin(c *gin.Context) { + // Obtain the POSTed username and password values + username := c.PostForm("username") + password := c.PostForm("password") + + var userDao = UserDAO{Db: dao.KalorienmanagerDb} + + // Check if the username/password combination is valid + if userDao.IsUserValid(username, password) { + sessionInterface, _ := c.Get("session") + sessionID := sessionInterface.(string) + user, _ := userDao.FindByUsername(username) + sessionDao := auth.SessionDAO{Db: dao.KalorienmanagerDb} + session, _ := sessionDao.FindByID(sessionID) + session.Username = username + session.IsAdmin = user.IsAdmin + sessionDao.Update(session) + util.Render(c, gin.H{"title": "Successful Login", "InfoMessage": "Login successfull"}, "kontor/index.html") + } else { + // If the username/password combination is invalid, + // show the error message on the login page + c.HTML(http.StatusBadRequest, "login.html", gin.H{ + "ErrorTitle": "Login Failed", + "ErrorMessage": "Invalid credentials provided"}) + } +} + +// Logout invalidates session. +func Logout(c *gin.Context) { + sessionInterface, _ := c.Get("session") + sessionID := sessionInterface.(string) + c.SetCookie("session", sessionID, -1, "", "", false, true) + + // Redirect to the home page + c.Redirect(http.StatusTemporaryRedirect, "/") +} + +func showAdminIndex(c *gin.Context) { + // Call the render function with the name of the template to render + util.Render(c, gin.H{"title": "Kontor", "payload": nil}, "kontor/admin.html") +} + +func showUserIndex(c *gin.Context) { + var dao = UserDAO{Db: dao.KalorienmanagerDb} + if users, err := dao.FindAll(); err == nil && users != nil { + util.Render(c, gin.H{"title": "Kontor User Administration", "payload": users}, "kontor/users.html") + } else { + util.Render(c, gin.H{"title": "Kontor User Administration", "payload": users, "ErrorMessage": err}, "kontor/users.html") + } +} + +func showUserDetails(c *gin.Context) { + userID := c.Param("userid") + var userDao = UserDAO{Db: dao.KalorienmanagerDb} + if user, err := userDao.FindByID(userID); err == nil && &user != nil { + util.Render(c, gin.H{"title": "Kontor User Administration", "payload": user, "action": util.SaveAction}, "kontor/user-detail.html") + } else { + c.AbortWithError(http.StatusNotFound, err) + } +} + +func showUserCreation(c *gin.Context) { + var user = User{} + util.Render(c, gin.H{"title": "Kontor User Administration", "payload": user, "action": util.AddAction}, "kontor/user-detail.html") +} + +func validateUserCreation(c *gin.Context) { + // Obtain the POSTed username and password values + username := c.PostForm("username") + firstname := c.PostForm("firstname") + lastname := c.PostForm("lastname") + password := c.PostForm("password") + adminFormVar := c.PostForm("admin") + action := c.PostForm("action") + userid := c.PostForm("userid") + isAdmin, _ := strconv.ParseBool(adminFormVar) + + var err error + var dao = UserDAO{Db: dao.KalorienmanagerDb} + var user = User{} + + switch action { + case util.AddAction: + user.Username = username + user.Firstname = firstname + user.Lastname = lastname + user.IsAdmin = isAdmin + user.Password, _ = HashPassword(password) + _, err = dao.Upsert(user) + case util.SaveAction: + user, _ = dao.FindByID(userid) + user.Username = username + user.Firstname = firstname + user.Lastname = lastname + user.IsAdmin = isAdmin + user.Password, _ = HashPassword(password) + err = dao.Update(user) + case util.DeleteAction: + user, _ = dao.FindByID(userid) + err = dao.Delete(user) + } + if err == nil { + c.Redirect(http.StatusTemporaryRedirect, "/admin/user") + } else { + c.HTML(http.StatusBadRequest, "kontor/create-user.html", gin.H{ + "ErrorTitle": "User Creation Failed", + "ErrorMessage": err.Error()}) + } +} diff --git a/pkg/auth/middleware.go b/pkg/auth/middleware.go new file mode 100644 index 0000000..de0d01f --- /dev/null +++ b/pkg/auth/middleware.go @@ -0,0 +1,85 @@ +package auth + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/properties" + "net/http" + + "github.com/gin-gonic/gin" + "gopkg.in/mgo.v2/bson" +) + +var sessionDao = SessionDAO{Db: dao.KalorienmanagerDb} + +// EnsureLoggedIn ensures that a request will be aborted with an error +// if the user is not logged in +func EnsureLoggedIn() gin.HandlerFunc { + return func(c *gin.Context) { + // If there's an error or if the token is empty + // the user is not logged in + sessionInterface, _ := c.Get("session") + sessionID := sessionInterface.(string) + if session, err := sessionDao.GetSession(sessionID); err != nil || session.Username == "" { + c.Redirect(http.StatusTemporaryRedirect, "/") + //c.AbortWithStatus(http.StatusUnauthorized) + } + } +} + +// EnsureAdminStatus ensures that a request will be aborted with an error +// if the user is not logged in +func EnsureAdminStatus() gin.HandlerFunc { + return func(c *gin.Context) { + // If there's an error or if the token is empty + // the user is not logged in + sessionInterface, _ := c.Get("session") + sessionID := sessionInterface.(string) + if session, err := sessionDao.GetSession(sessionID); err != nil || !session.IsAdmin { + c.Redirect(http.StatusTemporaryRedirect, "/") + //c.AbortWithStatus(http.StatusUnauthorized) + } + } +} + +// EnsureNotLoggedIn ensures that a request will be aborted with an error +// if the user is already logged in +func EnsureNotLoggedIn() gin.HandlerFunc { + return func(c *gin.Context) { + // If there's no error or if the token is not empty + // the user is already logged in + sessionInterface, _ := c.Get("session") + sessionID := sessionInterface.(string) + if session, err := sessionDao.GetSession(sessionID); err != nil || session.Username != "" { + c.Redirect(http.StatusTemporaryRedirect, "/") + //c.AbortWithStatus(http.StatusUnauthorized) + } + } +} + +// SetSessionStatus reads sessionId from cookie if available or create new session object +// and sets cookie accordingly. +func SetSessionStatus() gin.HandlerFunc { + return func(c *gin.Context) { + if sessionID, err := c.Cookie("session"); err == nil || sessionID != "" { + c.Set("session", sessionID) + } else { + session, _ := sessionDao.GetSession(bson.NewObjectId().Hex()) + sessionID := session.ID.Hex() + c.Set("session", sessionID) + c.SetCookie("session", sessionID, 3600, "", "", false, true) + } + } +} + +// SetSessionData populates sesion information with username, admin status of user and +// application version. +func SetSessionData(c *gin.Context, data gin.H) { + sessionInterface, _ := c.Get("session") + sessionID := sessionInterface.(string) + // TODO move PrintDebug("setSessionData(): %v", sessionId) + session, _ := sessionDao.GetSession(sessionID) + // TODO move PrintDebug("setSessionData(): %v", *session) + data["is_logged_in"] = (session.Username != "") + data["is_admin"] = session.IsAdmin + data["version"] = properties.Version +} diff --git a/pkg/auth/session.go b/pkg/auth/session.go new file mode 100644 index 0000000..dccf9fb --- /dev/null +++ b/pkg/auth/session.go @@ -0,0 +1,11 @@ +package auth + +import "gopkg.in/mgo.v2/bson" + +// Session defines the data model for sessions with id,user name and admin status. +type Session struct { + ID bson.ObjectId `json:"_id" bson:"_id,omitempty"` + Username string `json:"username" bson:"username,omitempty"` + IsAdmin bool `json:"is_admin" bson:"is_admin,omitempty"` + Model string `json:"model" bson:"model,omitempty"` +} diff --git a/pkg/auth/session_dao.go b/pkg/auth/session_dao.go new file mode 100644 index 0000000..b695d69 --- /dev/null +++ b/pkg/auth/session_dao.go @@ -0,0 +1,78 @@ +package auth + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + + mgo "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" +) + +// SessionDAO extends the type BaseDAO. +type SessionDAO struct { + Db dao.BaseDAO +} + +const ( + // SESSIONCOLLECTION defines the collection name for storing session data. + SESSIONCOLLECTION = "session" + // SESSIONMODEL defines the name of the session data model. + SESSIONMODEL = "kalorienmanager.admin.session" +) + +// FindAll retrieves the list of sessions from the database. +func (m *SessionDAO) FindAll() ([]Session, error) { + m.Db.Connect() + var sessions []Session + err := m.Db.MongoDb.C(SESSIONCOLLECTION).Find(bson.M{"model": SESSIONMODEL}).All(&sessions) + return sessions, err +} + +// FindByID returns a session with given id or returns the error. +func (m *SessionDAO) FindByID(id string) (Session, error) { + m.Db.Connect() + var session Session + err := m.Db.MongoDb.C(SESSIONCOLLECTION).FindId(bson.ObjectIdHex(id)).One(&session) + return session, err +} + +// Insert a session into database. +func (m *SessionDAO) Insert(session Session) error { + m.Db.Connect() + session.Model = SESSIONMODEL + err := m.Db.MongoDb.C(SESSIONCOLLECTION).Insert(&session) + //log.PrintDebug("Insert: %v, %v\n", session, err) + return err +} + +// Upsert a session into database. +func (m *SessionDAO) Upsert(session Session) (*mgo.ChangeInfo, error) { + m.Db.Connect() + session.Model = SESSIONMODEL + info, err := m.Db.MongoDb.C(SESSIONCOLLECTION).Upsert(bson.M{"_id": session.ID}, &session) + return info, err +} + +// Update an existing session. +func (m *SessionDAO) Update(session Session) error { + m.Db.Connect() + err := m.Db.MongoDb.C(SESSIONCOLLECTION).UpdateId(session.ID, &session) + return err +} + +// Delete an existing session. +func (m *SessionDAO) Delete(session Session) error { + m.Db.Connect() + err := m.Db.MongoDb.C(SESSIONCOLLECTION).Remove(&session) + return err +} + +// GetSession get a session by given id or create a new one, if nothing was found. +func (m *SessionDAO) GetSession(id string) (*Session, error) { + m.Db.Connect() + session, err := m.FindByID(id) + if err != nil { + session = Session{ID: bson.ObjectIdHex(id), Username: "", IsAdmin: false} + m.Insert(session) + } + return &session, nil +} diff --git a/pkg/auth/session_test.go b/pkg/auth/session_test.go new file mode 100644 index 0000000..188df94 --- /dev/null +++ b/pkg/auth/session_test.go @@ -0,0 +1,103 @@ +package auth + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + "reflect" + "testing" + + "gopkg.in/mgo.v2/bson" +) + +var sessionModelTestTable = []struct { + name string + typeName string +}{ + {"Id", "string"}, + {"Username", "string"}, + {"IsAdmin", "bool"}, + {"Model", "string"}, +} + +func TestSessionModel(t *testing.T) { + m := Session{} + if reflect.TypeOf(m).NumField() != len(sessionModelTestTable) { + t.Fail() + } + for index, testData := range sessionModelTestTable { + givenType := reflect.TypeOf(m).Field(index).Type.Kind().String() + if givenType != testData.typeName { + t.Fail() + } + } +} + +func TestListSessions(t *testing.T) { + var ( + sessionDao = SessionDAO{Db: dao.TestDb} + ) + sessions, err := sessionDao.FindAll() + if err != nil { + t.Fail() + } + if sessions != nil { + t.Fail() + } +} + +func TestInsertSession(t *testing.T) { + var ( + sessionDao = SessionDAO{Db: dao.TestDb} + session = Session{} + sessions []Session + ) + session.ID = bson.NewObjectId() + session.Username = "test" + err := sessionDao.Insert(session) + if err != nil { + t.Fail() + } + sessions, err = sessionDao.FindAll() + if err != nil { + t.Fail() + } + if len(sessions) != 1 { + t.Fail() + } +} + +func TestUpsertSession(t *testing.T) { + var ( + sessionDao = SessionDAO{Db: dao.TestDb} + session = Session{} + ) + session.ID = bson.NewObjectId() + session.Username = "test2" + sessionDao.Upsert(session) + sessions, err := sessionDao.FindAll() + if err != nil { + t.Fail() + } + if len(sessions) != 2 { + t.Fail() + } +} + +func TestDeleteSession(t *testing.T) { + var ( + sessionDao = SessionDAO{Db: dao.TestDb} + ) + sessions, err := sessionDao.FindAll() + if err != nil { + t.Fail() + } + for _, session := range sessions { + sessionDao.Delete(session) + } + sessions, err = sessionDao.FindAll() + if err != nil { + t.Fail() + } + if len(sessions) != 0 { + t.Fail() + } +} diff --git a/pkg/dao/database.go b/pkg/dao/database.go new file mode 100644 index 0000000..db2c185 --- /dev/null +++ b/pkg/dao/database.go @@ -0,0 +1,31 @@ +package dao + +import ( + "log" + + mgo "gopkg.in/mgo.v2" +) + +// BaseDAO definess the connection parameters to a MongoDB instance. +type BaseDAO struct { + Server string + Database string + MongoDb *mgo.Database +} + +var ( + // KalorienmanagerDb has the database connection to the productive MongoDB instance. + KalorienmanagerDb = BaseDAO{Server: "localhost", Database: "kalorienmanager"} + // TestDb has the database connection to the test MongoDB instance. + TestDb = BaseDAO{Server: "localhost", Database: "kalorienmanager_test"} +) + +// Connect instantiates the database session. +func (m *BaseDAO) Connect() { + session, err := mgo.Dial(m.Server) + if err != nil { + //util.PrintDebug("Connect: %v", err) + log.Fatal(err) + } + m.MongoDb = session.DB(m.Database) +} diff --git a/pkg/dao/database_test.go b/pkg/dao/database_test.go new file mode 100644 index 0000000..2890770 --- /dev/null +++ b/pkg/dao/database_test.go @@ -0,0 +1,50 @@ +package dao + +import ( + "reflect" + "testing" +) + +var baseDaoTestTable = []struct { + name string + typeName string +}{ + {"Server", "string"}, + {"Database", "string"}, + {"MongoDb", "ptr"}, +} + +func TestCheckBaseDao(t *testing.T) { + d := BaseDAO{} + for index, testData := range baseDaoTestTable { + givenType := reflect.TypeOf(d).Field(index).Type.Kind().String() + if givenType != testData.typeName { + t.Fail() + } + } +} + +func TestConnectDb(t *testing.T) { + d := BaseDAO{} + d.Connect() + if d.MongoDb == nil { + t.Fail() + } +} + +func TestDatabasesConfig(t *testing.T) { + kalorienmanagerDb := KalorienmanagerDb + if kalorienmanagerDb.Server != "localhost" { + t.Fail() + } + if kalorienmanagerDb.Database != "kalorienmanager" { + t.Fail() + } + testDb := TestDb + if testDb.Server != "localhost" { + t.Fail() + } + if testDb.Database != "kalorienmanager_test" { + t.Fail() + } +} diff --git a/pkg/properties/root.go b/pkg/properties/root.go new file mode 100644 index 0000000..c80e136 --- /dev/null +++ b/pkg/properties/root.go @@ -0,0 +1,25 @@ +package properties + +var ( + // Version defines the version of the web application kontor. + Version = "undefined" + // Debug defines the property debug to be used for more verbose output. + Debug = false + // Port defines port number under the web application is reachable. + Port = 8500 +) + +// SetVersion sets Version with given value. +func SetVersion(value string) { + Version = value +} + +// SetDebug sets Debug with given value. +func SetDebug(value bool) { + Debug = value +} + +// SetPort sets Port with given value. +func SetPort(value int) { + Port = value +} diff --git a/pkg/setup/data.go b/pkg/setup/data.go new file mode 100644 index 0000000..1fb6ba5 --- /dev/null +++ b/pkg/setup/data.go @@ -0,0 +1,11 @@ +package setup + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + "log" +) + +// CheckTradeYourSportsCardsData checks if the TYSC releated data is available. +func CheckFoodData() { + log.Printf("Check data for Food values") +} diff --git a/pkg/setup/routes.go b/pkg/setup/routes.go new file mode 100644 index 0000000..b11b16b --- /dev/null +++ b/pkg/setup/routes.go @@ -0,0 +1,28 @@ +package setup + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/admin" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/util" + + "github.com/gin-gonic/gin" +) + +// InitializeRoutes setup the routes for Kalorien Manager web application. +func InitializeRoutes(router *gin.Engine) { + + // Use the setUserStatus middleware for every route to set a flag + // indicating whether the request was from an authenticated user or not + router.Use(auth.SetSessionStatus()) + + // Handle the index route + router.GET("/", util.ShowIndexPage) + + userRoutes := router.Group("/user") + { + userRoutes.GET("/login", auth.EnsureNotLoggedIn(), admin.ShowLoginPage) + userRoutes.POST("/login", auth.EnsureNotLoggedIn(), admin.PerformLogin) + userRoutes.GET("/logout", auth.EnsureLoggedIn(), admin.Logout) + } + admin.GetRoutes(router) +} diff --git a/pkg/setup/session.go b/pkg/setup/session.go new file mode 100644 index 0000000..e29bfc7 --- /dev/null +++ b/pkg/setup/session.go @@ -0,0 +1,15 @@ +package setup + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" +) + +// CleanupSessions removes all sessions from database. +func CleanupSessions() { + sessionDao := auth.SessionDAO{Db: dao.KalorienmanagerDb} + sessions, _ := sessionDao.FindAll() + for _, session := range sessions { + sessionDao.Delete(session) + } +} diff --git a/pkg/setup/user.go b/pkg/setup/user.go new file mode 100644 index 0000000..de5ea1f --- /dev/null +++ b/pkg/setup/user.go @@ -0,0 +1,20 @@ +package setup + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/admin" + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/dao" + + "gopkg.in/mgo.v2/bson" +) + +// CheckUserList ensures that at least the admin user is available. +func CheckUserList() { + var userDao = admin.UserDAO{Db: dao.KalorienmanagerDb} + users, err := userDao.FindAll() + if err == nil && len(users) == 0 { + password, _ := admin.HashPassword("admin") + id := bson.NewObjectId() + user := admin.User{ID: id, Username: "admin", Password: password, Firstname: "Administrator", IsAdmin: true} + userDao.Insert(user) + } +} diff --git a/pkg/util/render.go b/pkg/util/render.go new file mode 100644 index 0000000..b83f11b --- /dev/null +++ b/pkg/util/render.go @@ -0,0 +1,39 @@ +package util + +import ( + "gitlab.ingenieurbuero-peetz.de/tpeetz/kalorienmanager.git/pkg/auth" + "net/http" + + "github.com/gin-gonic/gin" +) + +const ( + // SaveAction defines label of button. + SaveAction = "Save" + // AddAction defines label of button. + AddAction = "Add" + // DeleteAction defines label of button. + DeleteAction = "Delete" +) + +// Render one of HTML, JSON or CSV based on the 'Accept' header of the request +// If the header doesn't specify this, HTML is rendered, provided that +// the template name is present +func Render(c *gin.Context, data gin.H, templateName string) { + auth.SetSessionData(c, data) + switch c.Request.Header.Get("Accept") { + case "application/json": + c.JSON(http.StatusOK, data["payload"]) + case "application/xml": + c.XML(http.StatusOK, data["payload"]) + default: + c.HTML(http.StatusOK, templateName, data) + } +} + +// ShowIndexPage render the index page of Kontor web application. +func ShowIndexPage(c *gin.Context) { + // Call the render function with the name of the template to render + //log.Printf("Context: %v", c) + Render(c, gin.H{"title": "Kalorienmanager", "payload": nil}, "index.html") +}