diff --git a/go/default.nix b/go/default.nix index e0cba0846f..034f521e1d 100644 --- a/go/default.nix +++ b/go/default.nix @@ -14,4 +14,5 @@ args: { tokend = import ./tokend args; access = import ./access args; vault = import ./vault args; + tumblrandom = import ./tumblrandom args; } diff --git a/go/go.mod b/go/go.mod index ef28917358..6e2f06320d 100644 --- a/go/go.mod +++ b/go/go.mod @@ -17,16 +17,93 @@ require ( github.com/hashicorp/vault/api v1.4.1 github.com/jackc/pgtype v1.4.2 github.com/jackc/pgx/v4 v4.8.1 + github.com/jamespfennell/xz v0.1.2 + github.com/mattn/go-sqlite3 v1.14.15 github.com/numtide/go-nix v0.0.0-20210617093959-c74bbf961f08 github.com/pomerium/sdk-go v0.0.5 github.com/prometheus/client_golang v1.12.1 gocloud.dev v0.24.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 ) require ( - github.com/mattn/go-sqlite3 v1.14.15 // indirect - github.com/ulikunitz/xz v0.5.10 // indirect + cloud.google.com/go v0.94.0 // indirect + cloud.google.com/go/storage v1.16.1 // indirect + github.com/armon/go-metrics v0.3.9 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/aws/aws-sdk-go v1.40.34 // indirect + github.com/aws/aws-sdk-go-v2 v1.9.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.7.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.4.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect + github.com/aws/smithy-go v1.8.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/fatih/color v1.7.0 // indirect + github.com/godbus/dbus/v5 v5.0.4 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/wire v0.5.0 // indirect + github.com/googleapis/gax-go/v2 v2.1.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v0.16.2 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-version v1.2.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/sdk v0.4.1 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.6.4 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.0.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/puddle v1.1.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/api v0.56.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect + google.golang.org/grpc v1.41.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect ) diff --git a/go/go.sum b/go/go.sum index 0dff73ba78..7700f5a14b 100644 --- a/go/go.sum +++ b/go/go.sum @@ -73,34 +73,27 @@ github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEew github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs= github.com/Azure/go-amqp v0.13.11/go.mod h1:D5ZrjQqB1dyp1A+G73xeL/kNn7D5qHJIIsNNps7YNmk= github.com/Azure/go-amqp v0.13.12/go.mod h1:D5ZrjQqB1dyp1A+G73xeL/kNn7D5qHJIIsNNps7YNmk= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.20 h1:s8H1PbCZSqg/DH7JMlOz6YMig6htWLNPsjDdlLqCx3M= github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.15 h1:X+p2GF0GWyOiSmqohIaEeuNFNDY4I4EOlVuUQvFdWMk= github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= github.com/Azure/go-autorest/autorest/azure/cli v0.4.3/go.mod h1:yAQ2b6eP/CmLPnmLvxtT1ALIY3OR1oFcCqVBi8vHiTc= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 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= @@ -154,7 +147,6 @@ github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3 github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -173,7 +165,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -241,7 +232,6 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -398,7 +388,6 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -417,7 +406,6 @@ github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye47 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -450,6 +438,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1 h1:PJAw7H/9hoWC4Kf3J8iNmL1SwA6E8vfsLqBiL+F6CtI= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jamespfennell/xz v0.1.2 h1:iCw5kScLfGCceOKgQaGuj5RilAAlV4iiwauYntak2oU= +github.com/jamespfennell/xz v0.1.2/go.mod h1:DhpWvZY1xDkK/6BREFl3c3R/fZh7IBdYq2m7xh4uLl0= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= @@ -608,8 +598,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= -github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1076,7 +1064,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= diff --git a/go/tumblrandom/default.nix b/go/tumblrandom/default.nix new file mode 100644 index 0000000000..e45bea9d37 --- /dev/null +++ b/go/tumblrandom/default.nix @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2023 Luke Granger-Brown +# +# SPDX-License-Identifier: Apache-2.0 + +{ depot, ... }: +depot.third_party.buildGo.program { + name = "tumblrandom"; + srcs = [ + ./tumblrandom.go + ]; + deps = with depot; [ + third_party.gopkgs."golang.org".x.oauth2 + ]; +} diff --git a/go/tumblrandom/tumblrandom.go b/go/tumblrandom/tumblrandom.go new file mode 100644 index 0000000000..cc4163d1dc --- /dev/null +++ b/go/tumblrandom/tumblrandom.go @@ -0,0 +1,353 @@ +package main + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "io/fs" + "log" + mathrand "math/rand" + "net" + "net/http" + "os" + "os/signal" + "path/filepath" + "strings" + + "golang.org/x/oauth2" +) + +var ( + debugOnlyAssumeEmail = flag.String("debug_only_assume_email", "", "If set, ignores the x-pomerium-claim-email header and just uses this static address.") + addr = flag.String("addr", "localhost:10908", "Port to listen on.") + dataDir = flag.String("data_dir", os.Getenv("STATE_DIRECTORY"), "Data directory to store data in.") + + oauthClientID = flag.String("oauth_client_id", os.Getenv("OAUTH_CLIENT_ID"), "Sets the OAuth client ID to use.") + oauthClientSecret = flag.String("oauth_client_secret", os.Getenv("OAUTH_CLIENT_SECRET"), "Sets the OAuth client secret to use.") + baseURL = flag.String("base_url", "", "Sets my publically-visible base domain.") + + httpMux = http.NewServeMux() +) + +type Post struct { + PostURL string `json:"post_url"` +} + +type User struct { + originPath string + email string + + OAuthToken *oauth2.Token `json:"oauth_token"` + Likes []Post `json:"likes"` +} + +func (u *User) save() error { + p := u.originPath + if p == "" { + return fmt.Errorf("originPath somehow unset") + } + + bs, err := json.Marshal(u) + if err != nil { + return err + } + return os.WriteFile(p, bs, 0600) +} + +func pathForUser(email, dataDir string) string { + sum := sha256.Sum256([]byte(email)) + return filepath.Join(dataDir, hex.EncodeToString(sum[:])+".json") +} + +func loadUser(email, dataDir string) (*User, error) { + p := pathForUser(email, dataDir) + u := &User{originPath: p, email: email} + + d, err := os.ReadFile(p) + if errors.Is(err, fs.ErrNotExist) { + log.Printf("generating new user for %v (path= %v)", email, p) + return u, nil + } + if err := json.Unmarshal(d, u); err != nil { + return nil, fmt.Errorf("unmarshalling JSON from %v: %w", p, err) + } + log.Printf("loaded user for %v from %v", email, p) + return u, nil +} + +const ( + pomeriumEmailHeader = "x-pomerium-claim-email" + oauthStateCookie = "tumblr-oauth-state" +) + +type contextKeyType struct{} + +var ( + userContextKey = contextKeyType{} +) + +func user(ctx context.Context) (*User, bool) { + u, ok := ctx.Value(userContextKey).(*User) + return u, ok +} + +type app struct { + oauthConfig *oauth2.Config + dataDir string + debugOnlyAssumeEmail string +} + +func (a *app) loadUserMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + email := a.debugOnlyAssumeEmail + if email == "" { + email = r.Header.Get(pomeriumEmailHeader) + } + if email == "" { + http.Error(rw, "no email", http.StatusForbidden) + return + } + + u, err := loadUser(email, a.dataDir) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), userContextKey, u))) + }) +} + +func (a *app) performLogin(ctx context.Context, u *User, rw http.ResponseWriter, r *http.Request) { + randData := make([]byte, 32) + if n, err := rand.Read(randData); err != nil { + http.Error(rw, "bad random: "+err.Error(), http.StatusInternalServerError) + return + } else if n != len(randData) { + http.Error(rw, fmt.Sprintf("bad random: got %d of random not %d", n, len(randData)), http.StatusInternalServerError) + return + } + stateVal := hex.EncodeToString(randData) + http.SetCookie(rw, &http.Cookie{ + Name: oauthStateCookie, + Value: stateVal, + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + }) + http.Redirect(rw, r, a.oauthConfig.AuthCodeURL(stateVal, oauth2.AccessTypeOnline), http.StatusSeeOther) +} + +func (a *app) handleOAuth(ctx context.Context, u *User, rw http.ResponseWriter, r *http.Request) { + cookie, err := r.Cookie(oauthStateCookie) + if err != nil { + http.Error(rw, fmt.Sprintf("getting state cookie: %v", err), http.StatusBadRequest) + return + } + if cookie.Value != r.URL.Query().Get("state") { + http.Error(rw, "invalid state", http.StatusBadRequest) + return + } + + tok, err := a.oauthConfig.Exchange(ctx, r.URL.Query().Get("code")) + if err != nil { + http.Error(rw, fmt.Sprintf("oauth2 exchange: %v", err), http.StatusInternalServerError) + return + } + + u.OAuthToken = tok + if err := u.save(); err != nil { + http.Error(rw, fmt.Sprintf("persisting user: %v", err), http.StatusInternalServerError) + return + } + + http.Redirect(rw, r, "/refresh", http.StatusSeeOther) +} + +func (a *app) redirectToLogin(rw http.ResponseWriter, r *http.Request) { + http.Redirect(rw, r, "/login", http.StatusSeeOther) +} + +func (a *app) requireOAuthMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + u, ok := user(ctx) + if !ok { + http.Error(rw, "user missing", http.StatusInternalServerError) + } else if r.URL.Path == "/login" { + a.performLogin(ctx, u, rw, r) + } else if r.URL.Path == "/oauth" { + a.handleOAuth(ctx, u, rw, r) + } else if u == nil || (len(u.Likes) == 0 && (u.OAuthToken == nil || !u.OAuthToken.Valid())) { + a.redirectToLogin(rw, r) + } else { + next.ServeHTTP(rw, r) + } + }) +} + +type likesResponse struct { + Response struct { + LikedPosts []Post `json:"liked_posts"` + Links struct { + Next struct { + Href string `json:"href"` + } `json:"next"` + } `json:"_links"` + } `json:"response"` +} + +func (a *app) refreshLikes(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + u, ok := user(ctx) + if !ok || !u.OAuthToken.Valid() { + a.redirectToLogin(rw, r) + return + } + + client := a.oauthConfig.Client(ctx, u.OAuthToken) + + var likes []Post + const urlPrefix = "https://api.tumblr.com" + urlSuffix := "/v2/user/likes" + for { + req, err := http.NewRequestWithContext(ctx, "GET", urlPrefix+urlSuffix, nil) + if err != nil { + http.Error(rw, fmt.Sprintf("formulating likes request: %v", err), http.StatusInternalServerError) + return + } + fmt.Println(req.URL) + resp, err := client.Do(req) + if err != nil { + http.Error(rw, fmt.Sprintf("performing likes request: %v", err), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + errBody, _ := io.ReadAll(resp.Body) + http.Error(rw, fmt.Sprintf("likes request status %v\n\n%v", resp.StatusCode, string(errBody)), http.StatusInternalServerError) + return + } + + var r likesResponse + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + http.Error(rw, fmt.Sprintf("decoding likes body as json: %v", err), http.StatusInternalServerError) + return + } + likes = append(likes, r.Response.LikedPosts...) + + urlSuffix = r.Response.Links.Next.Href + if urlSuffix == "" { + break + } + } + + u.Likes = likes + if err := u.save(); err != nil { + http.Error(rw, fmt.Sprintf("saving likes: %v", err), http.StatusInternalServerError) + return + } + + http.Redirect(rw, r, "/?refreshed=true", http.StatusSeeOther) + return +} + +func (a *app) index(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + u, ok := user(ctx) + if !ok { + a.redirectToLogin(rw, r) + return + } + + fmt.Fprintf(rw, ` + +

%d likes

+

random

+
+
+
+
+refresh +`, len(u.Likes)) +} + +func (a *app) random(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + u, ok := user(ctx) + if !ok { + a.redirectToLogin(rw, r) + return + } + if len(u.Likes) == 0 { + http.Redirect(rw, r, "/", http.StatusSeeOther) + return + } + + n := mathrand.Intn(len(u.Likes)) + l := u.Likes[n] + http.Redirect(rw, r, l.PostURL, http.StatusSeeOther) +} + +func main() { + flag.Parse() + log.Println("using oauth client ID", *oauthClientID) + a := &app{ + oauthConfig: &oauth2.Config{ + ClientID: *oauthClientID, + ClientSecret: *oauthClientSecret, + Endpoint: oauth2.Endpoint{ + AuthURL: "https://www.tumblr.com/oauth2/authorize", + TokenURL: "https://api.tumblr.com/v2/oauth2/token", + }, + Scopes: []string{"basic"}, + RedirectURL: fmt.Sprintf("%s/oauth", *baseURL), + }, + dataDir: *dataDir, + debugOnlyAssumeEmail: *debugOnlyAssumeEmail, + } + httpMux.HandleFunc("/refresh", a.refreshLikes) + httpMux.HandleFunc("/", a.index) + httpMux.HandleFunc("/random", a.random) + + s := &http.Server{ + Handler: a.loadUserMiddleware(a.requireOAuthMiddleware(httpMux)), + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + for _, singleAddr := range strings.Split(*addr, ",") { + singleAddr := singleAddr + go func() { + l, err := net.Listen("tcp", singleAddr) + if err != nil { + log.Printf("listen on %v: %v", singleAddr, err) + cancel() + return + } + log.Printf("starting to serve on %v", singleAddr) + if err := s.Serve(l); err != http.ErrServerClosed { + log.Printf("HTTP server ListenAndServe: %v", err) + cancel() + } + }() + } + + ctx, stop := signal.NotifyContext(ctx, os.Interrupt) + defer stop() + + select { + case <-ctx.Done(): + log.Println("shutting down gracefully (SIGINT again to stop immediately)") + stop() + if err := s.Shutdown(context.Background()); err != nil { + log.Printf("HTTP server Shutdown: %v", err) + } + } +}