mirror of
https://github.com/vwxyzjn/portwarden
synced 2025-12-10 13:23:34 +00:00
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
vendor
|
||||||
|
Dockerfile
|
||||||
31
Dockerfile
Normal file
31
Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
FROM ubuntu:latest
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
RUN apt-get update && apt-get install -y wget git gcc unzip
|
||||||
|
RUN wget -q -P /tmp https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz
|
||||||
|
RUN tar -C /usr/local -xzf /tmp/go1.11.2.linux-amd64.tar.gz
|
||||||
|
RUN rm /tmp/go1.11.2.linux-amd64.tar.gz
|
||||||
|
ENV GOPATH /go
|
||||||
|
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
||||||
|
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
|
||||||
|
|
||||||
|
# Setup work directory
|
||||||
|
COPY . /go/src/github.com/vwxyzjn/portwarden
|
||||||
|
WORKDIR /go/src/github.com/vwxyzjn/portwarden
|
||||||
|
|
||||||
|
# Install Go Dep
|
||||||
|
RUN wget -q https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64
|
||||||
|
RUN mv dep-linux-amd64 /usr/bin/dep
|
||||||
|
RUN chmod +x /usr/bin/dep
|
||||||
|
|
||||||
|
# Install Bitwarden CLI
|
||||||
|
RUN wget -q https://github.com/bitwarden/cli/releases/download/v1.6.0/bw-linux-1.6.0.zip
|
||||||
|
RUN unzip bw-linux-1.6.0.zip -d /usr/bin/
|
||||||
|
RUN chmod +x /usr/bin/bw
|
||||||
|
|
||||||
|
# Run dep
|
||||||
|
# Notice git is the dependency for running dep
|
||||||
|
RUN cd /go/src/github.com/vwxyzjn/portwarden && dep ensure --vendor-only
|
||||||
|
|
||||||
|
# Ready to run
|
||||||
|
EXPOSE 5000
|
||||||
46
Dockerfile.Build
Normal file
46
Dockerfile.Build
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
FROM ubuntu:latest
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
RUN apt-get update && apt-get install -y wget git gcc unzip
|
||||||
|
RUN wget -q -P /tmp https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz
|
||||||
|
RUN tar -C /usr/local -xzf /tmp/go1.11.2.linux-amd64.tar.gz
|
||||||
|
RUN rm /tmp/go1.11.2.linux-amd64.tar.gz
|
||||||
|
ENV GOPATH /go
|
||||||
|
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
||||||
|
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
|
||||||
|
|
||||||
|
# Setup work directory
|
||||||
|
COPY . /go/src/github.com/vwxyzjn/portwarden
|
||||||
|
WORKDIR /go/src/github.com/vwxyzjn/portwarden
|
||||||
|
|
||||||
|
# Install Go Dep
|
||||||
|
RUN wget -q https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64
|
||||||
|
RUN mv dep-linux-amd64 /usr/bin/dep
|
||||||
|
RUN chmod +x /usr/bin/dep
|
||||||
|
|
||||||
|
# Install Bitwarden CLI
|
||||||
|
RUN wget -q https://github.com/bitwarden/cli/releases/download/v1.6.0/bw-linux-1.6.0.zip
|
||||||
|
RUN unzip bw-linux-1.6.0.zip -d /usr/bin/
|
||||||
|
RUN chmod +x /usr/bin/bw
|
||||||
|
|
||||||
|
# Run dep
|
||||||
|
# Notice git is the dependency for running dep
|
||||||
|
RUN cd /go/src/github.com/vwxyzjn/portwarden && dep ensure --vendor-only
|
||||||
|
|
||||||
|
RUN go build /go/src/github.com/vwxyzjn/portwarden/web/worker/main.go && mv ./main /worker
|
||||||
|
RUN go build /go/src/github.com/vwxyzjn/portwarden/web/scheduler/main.go && mv ./main /scheduler
|
||||||
|
|
||||||
|
# Ready to run
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
|
||||||
|
FROM debian:stretch-20181112
|
||||||
|
RUN apt-get update && apt-get install -y ca-certificates openssl
|
||||||
|
COPY --from=0 /usr/bin/bw /usr/bin/bw
|
||||||
|
COPY --from=0 /scheduler /go/src/github.com/vwxyzjn/portwarden/web/scheduler/scheduler
|
||||||
|
COPY --from=0 /worker /go/src/github.com/vwxyzjn/portwarden/web/worker/worker
|
||||||
|
COPY --from=0 /go/src/github.com/vwxyzjn/portwarden/web/portwardenCredentials.json /go/src/github.com/vwxyzjn/portwarden/web/portwardenCredentials.json
|
||||||
|
RUN chmod +x /go/src/github.com/vwxyzjn/portwarden/web/scheduler/scheduler
|
||||||
|
RUN chmod +x /go/src/github.com/vwxyzjn/portwarden/web/worker/worker
|
||||||
|
WORKDIR /go/src/github.com/vwxyzjn/portwarden
|
||||||
|
EXPOSE 5000
|
||||||
319
Gopkg.lock
generated
319
Gopkg.lock
generated
@@ -3,15 +3,107 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "cloud.google.com/go"
|
name = "cloud.google.com/go"
|
||||||
packages = ["compute/metadata"]
|
packages = [
|
||||||
|
"compute/metadata",
|
||||||
|
"iam",
|
||||||
|
"internal/optional",
|
||||||
|
"internal/version",
|
||||||
|
"pubsub",
|
||||||
|
"pubsub/apiv1",
|
||||||
|
"pubsub/internal/distribution"
|
||||||
|
]
|
||||||
revision = "74b12019e2aa53ec27882158f59192d7cd6d1998"
|
revision = "74b12019e2aa53ec27882158f59192d7cd6d1998"
|
||||||
version = "v0.33.1"
|
version = "v0.33.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/antonholmquist/jason"
|
branch = "master"
|
||||||
|
name = "github.com/RichardKnop/logging"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "c23cef7eaa75a6a5b8810120e167bd590d8fd2ab"
|
revision = "b1d5d44c82d6c9fb6c8e8dad28412163586fd8ac"
|
||||||
version = "v1.0.0"
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/RichardKnop/machinery"
|
||||||
|
packages = [
|
||||||
|
"v1",
|
||||||
|
"v1/backends/amqp",
|
||||||
|
"v1/backends/dynamodb",
|
||||||
|
"v1/backends/eager",
|
||||||
|
"v1/backends/iface",
|
||||||
|
"v1/backends/memcache",
|
||||||
|
"v1/backends/mongo",
|
||||||
|
"v1/backends/redis",
|
||||||
|
"v1/backends/result",
|
||||||
|
"v1/brokers/amqp",
|
||||||
|
"v1/brokers/eager",
|
||||||
|
"v1/brokers/errs",
|
||||||
|
"v1/brokers/gcppubsub",
|
||||||
|
"v1/brokers/iface",
|
||||||
|
"v1/brokers/redis",
|
||||||
|
"v1/brokers/sqs",
|
||||||
|
"v1/common",
|
||||||
|
"v1/config",
|
||||||
|
"v1/log",
|
||||||
|
"v1/retry",
|
||||||
|
"v1/tasks",
|
||||||
|
"v1/tracing"
|
||||||
|
]
|
||||||
|
revision = "21f6dd0def9983b08c773554c89f64b0176da3a2"
|
||||||
|
version = "v1.5.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/RichardKnop/redsync"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "54b27db64c180751e7049321a2e109ff220ccdcc"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/aws/aws-sdk-go"
|
||||||
|
packages = [
|
||||||
|
"aws",
|
||||||
|
"aws/awserr",
|
||||||
|
"aws/awsutil",
|
||||||
|
"aws/client",
|
||||||
|
"aws/client/metadata",
|
||||||
|
"aws/corehandlers",
|
||||||
|
"aws/credentials",
|
||||||
|
"aws/credentials/ec2rolecreds",
|
||||||
|
"aws/credentials/endpointcreds",
|
||||||
|
"aws/credentials/stscreds",
|
||||||
|
"aws/crr",
|
||||||
|
"aws/csm",
|
||||||
|
"aws/defaults",
|
||||||
|
"aws/ec2metadata",
|
||||||
|
"aws/endpoints",
|
||||||
|
"aws/request",
|
||||||
|
"aws/session",
|
||||||
|
"aws/signer/v4",
|
||||||
|
"internal/ini",
|
||||||
|
"internal/sdkio",
|
||||||
|
"internal/sdkrand",
|
||||||
|
"internal/sdkuri",
|
||||||
|
"internal/shareddefaults",
|
||||||
|
"private/protocol",
|
||||||
|
"private/protocol/json/jsonutil",
|
||||||
|
"private/protocol/jsonrpc",
|
||||||
|
"private/protocol/query",
|
||||||
|
"private/protocol/query/queryutil",
|
||||||
|
"private/protocol/rest",
|
||||||
|
"private/protocol/xml/xmlutil",
|
||||||
|
"service/dynamodb",
|
||||||
|
"service/dynamodb/dynamodbattribute",
|
||||||
|
"service/dynamodb/dynamodbiface",
|
||||||
|
"service/sqs",
|
||||||
|
"service/sqs/sqsiface",
|
||||||
|
"service/sts"
|
||||||
|
]
|
||||||
|
revision = "08df30d135d32f1eb21bb7754eb042aeca521a4b"
|
||||||
|
version = "v1.15.84"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/bradfitz/gomemcache"
|
||||||
|
packages = ["memcache"]
|
||||||
|
revision = "bc664df9673713a0ccf26e3b55a673ec7301088b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
@@ -32,12 +124,6 @@
|
|||||||
]
|
]
|
||||||
revision = "cc9eb1d7ad760af14e8f918698f745e80377af4f"
|
revision = "cc9eb1d7ad760af14e8f918698f745e80377af4f"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/gin-contrib/cors"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "cf4846e6a636a76237a28d9286f163c132e841bc"
|
|
||||||
version = "v1.2"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/gin-contrib/sse"
|
name = "github.com/gin-contrib/sse"
|
||||||
@@ -55,9 +141,32 @@
|
|||||||
revision = "b869fe1415e4b9eb52f247441830d502aece2d4d"
|
revision = "b869fe1415e4b9eb52f247441830d502aece2d4d"
|
||||||
version = "v1.3.0"
|
version = "v1.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-redis/redis"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"internal",
|
||||||
|
"internal/consistenthash",
|
||||||
|
"internal/hashtag",
|
||||||
|
"internal/pool",
|
||||||
|
"internal/proto",
|
||||||
|
"internal/singleflight",
|
||||||
|
"internal/util"
|
||||||
|
]
|
||||||
|
revision = "b3d9bf10f6666b2ee5100a6f3f84f4caf3b4e37d"
|
||||||
|
version = "v6.14.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
packages = ["proto"]
|
packages = [
|
||||||
|
"proto",
|
||||||
|
"protoc-gen-go/descriptor",
|
||||||
|
"ptypes",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/duration",
|
||||||
|
"ptypes/empty",
|
||||||
|
"ptypes/timestamp"
|
||||||
|
]
|
||||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||||
version = "v1.2.0"
|
version = "v1.2.0"
|
||||||
|
|
||||||
@@ -67,12 +176,50 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
|
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/gomodule/redigo"
|
||||||
|
packages = [
|
||||||
|
"internal",
|
||||||
|
"redis"
|
||||||
|
]
|
||||||
|
revision = "9c11da706d9b7902c6da69c592f75637793fe121"
|
||||||
|
version = "v2.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/google/uuid"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/googleapis/gax-go"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "b001040cd31805261cbd978842099e326dfa857b"
|
||||||
|
version = "v2.0.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/imdario/mergo"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"
|
||||||
|
version = "v0.3.6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/jmespath/go-jmespath"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0b12d6b5"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/json-iterator/go"
|
name = "github.com/json-iterator/go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "1624edc4454b8682399def8740d46db5e4362ba4"
|
revision = "1624edc4454b8682399def8740d46db5e4362ba4"
|
||||||
version = "v1.1.5"
|
version = "v1.1.5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/kelseyhightower/envconfig"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "f611eb38b3875cc3bd991ca91c51d06446afa14c"
|
||||||
|
version = "v1.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/mattn/go-isatty"
|
name = "github.com/mattn/go-isatty"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -103,6 +250,16 @@
|
|||||||
revision = "cc3e4b2381762c56dbcdf9f93be03349a1dc1c14"
|
revision = "cc3e4b2381762c56dbcdf9f93be03349a1dc1c14"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/opentracing/opentracing-go"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"ext",
|
||||||
|
"log"
|
||||||
|
]
|
||||||
|
revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
|
||||||
|
version = "v1.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/pierrec/lz4"
|
name = "github.com/pierrec/lz4"
|
||||||
packages = [
|
packages = [
|
||||||
@@ -112,6 +269,12 @@
|
|||||||
revision = "635575b42742856941dbc767b44905bb9ba083f6"
|
revision = "635575b42742856941dbc767b44905bb9ba083f6"
|
||||||
version = "v2.0.7"
|
version = "v2.0.7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/streadway/amqp"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "27835f1a64e97101d95306211f03c0620ffa295d"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/tidwall/pretty"
|
name = "github.com/tidwall/pretty"
|
||||||
@@ -135,6 +298,28 @@
|
|||||||
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
|
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
|
||||||
version = "v0.5.4"
|
version = "v0.5.4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "go.opencensus.io"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"exemplar",
|
||||||
|
"internal",
|
||||||
|
"internal/tagencoding",
|
||||||
|
"plugin/ocgrpc",
|
||||||
|
"plugin/ochttp",
|
||||||
|
"plugin/ochttp/propagation/b3",
|
||||||
|
"stats",
|
||||||
|
"stats/internal",
|
||||||
|
"stats/view",
|
||||||
|
"tag",
|
||||||
|
"trace",
|
||||||
|
"trace/internal",
|
||||||
|
"trace/propagation",
|
||||||
|
"trace/tracestate"
|
||||||
|
]
|
||||||
|
revision = "b7bf3cdb64150a8c8c53b769fdeb2ba581bd4d4b"
|
||||||
|
version = "v0.18.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
@@ -146,7 +331,13 @@
|
|||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
packages = [
|
packages = [
|
||||||
"context",
|
"context",
|
||||||
"context/ctxhttp"
|
"context/ctxhttp",
|
||||||
|
"http/httpguts",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna",
|
||||||
|
"internal/timeseries",
|
||||||
|
"trace"
|
||||||
]
|
]
|
||||||
revision = "adae6a3d119ae4890b46832a2e88a95adc62b8e7"
|
revision = "adae6a3d119ae4890b46832a2e88a95adc62b8e7"
|
||||||
|
|
||||||
@@ -162,21 +353,59 @@
|
|||||||
]
|
]
|
||||||
revision = "f42d05182288abf10faef86d16c0d07b8d40ea2d"
|
revision = "f42d05182288abf10faef86d16c0d07b8d40ea2d"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sync"
|
||||||
|
packages = [
|
||||||
|
"errgroup",
|
||||||
|
"semaphore"
|
||||||
|
]
|
||||||
|
revision = "42b317875d0fa942474b76e1b46a6060d720ae6e"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix"]
|
||||||
revision = "66b7b1311ac80bbafcd2daeef9a5e6e2cd1e2399"
|
revision = "66b7b1311ac80bbafcd2daeef9a5e6e2cd1e2399"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "golang.org/x/text"
|
||||||
|
packages = [
|
||||||
|
"collate",
|
||||||
|
"collate/build",
|
||||||
|
"internal/colltab",
|
||||||
|
"internal/gen",
|
||||||
|
"internal/tag",
|
||||||
|
"internal/triegen",
|
||||||
|
"internal/ucd",
|
||||||
|
"language",
|
||||||
|
"secure/bidirule",
|
||||||
|
"transform",
|
||||||
|
"unicode/bidi",
|
||||||
|
"unicode/cldr",
|
||||||
|
"unicode/norm",
|
||||||
|
"unicode/rangetable"
|
||||||
|
]
|
||||||
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/api"
|
name = "google.golang.org/api"
|
||||||
packages = [
|
packages = [
|
||||||
"drive/v2",
|
"drive/v2",
|
||||||
"drive/v3",
|
|
||||||
"gensupport",
|
"gensupport",
|
||||||
"googleapi",
|
"googleapi",
|
||||||
"googleapi/internal/uritemplates"
|
"googleapi/internal/uritemplates",
|
||||||
|
"googleapi/transport",
|
||||||
|
"internal",
|
||||||
|
"iterator",
|
||||||
|
"option",
|
||||||
|
"support/bundler",
|
||||||
|
"transport",
|
||||||
|
"transport/grpc",
|
||||||
|
"transport/http",
|
||||||
|
"transport/http/internal/propagation"
|
||||||
]
|
]
|
||||||
revision = "83a9d304b1e613fc253e1e2710778642fe81af53"
|
revision = "83a9d304b1e613fc253e1e2710778642fe81af53"
|
||||||
|
|
||||||
@@ -191,18 +420,78 @@
|
|||||||
"internal/log",
|
"internal/log",
|
||||||
"internal/modules",
|
"internal/modules",
|
||||||
"internal/remote_api",
|
"internal/remote_api",
|
||||||
|
"internal/socket",
|
||||||
"internal/urlfetch",
|
"internal/urlfetch",
|
||||||
|
"socket",
|
||||||
"urlfetch"
|
"urlfetch"
|
||||||
]
|
]
|
||||||
revision = "4a4468ece617fc8205e99368fa2200e9d1fad421"
|
revision = "4a4468ece617fc8205e99368fa2200e9d1fad421"
|
||||||
version = "v1.3.0"
|
version = "v1.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
packages = [
|
||||||
|
"googleapis/api/annotations",
|
||||||
|
"googleapis/iam/v1",
|
||||||
|
"googleapis/pubsub/v1",
|
||||||
|
"googleapis/rpc/status",
|
||||||
|
"protobuf/field_mask"
|
||||||
|
]
|
||||||
|
revision = "31ac5d88444a9e7ad18077db9a165d793ad06a2e"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"balancer",
|
||||||
|
"balancer/base",
|
||||||
|
"balancer/roundrobin",
|
||||||
|
"codes",
|
||||||
|
"connectivity",
|
||||||
|
"credentials",
|
||||||
|
"credentials/oauth",
|
||||||
|
"encoding",
|
||||||
|
"encoding/proto",
|
||||||
|
"grpclog",
|
||||||
|
"internal",
|
||||||
|
"internal/backoff",
|
||||||
|
"internal/channelz",
|
||||||
|
"internal/envconfig",
|
||||||
|
"internal/grpcrand",
|
||||||
|
"internal/transport",
|
||||||
|
"keepalive",
|
||||||
|
"metadata",
|
||||||
|
"naming",
|
||||||
|
"peer",
|
||||||
|
"resolver",
|
||||||
|
"resolver/dns",
|
||||||
|
"resolver/passthrough",
|
||||||
|
"stats",
|
||||||
|
"status",
|
||||||
|
"tap"
|
||||||
|
]
|
||||||
|
revision = "2e463a05d100327ca47ac218281906921038fd95"
|
||||||
|
version = "v1.16.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/go-playground/validator.v8"
|
name = "gopkg.in/go-playground/validator.v8"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
|
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
|
||||||
version = "v8.18.2"
|
version = "v8.18.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "v2"
|
||||||
|
name = "gopkg.in/mgo.v2"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"bson",
|
||||||
|
"internal/json",
|
||||||
|
"internal/sasl",
|
||||||
|
"internal/scram"
|
||||||
|
]
|
||||||
|
revision = "9856a29383ce1c59f308dd1cf0363a79b5bef6b5"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/urfave/cli.v1"
|
name = "gopkg.in/urfave/cli.v1"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -218,6 +507,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "e94795c347f48a9ee90b68d79ae43762c0504d74ca19276f92b7c394faeab698"
|
inputs-digest = "b0982d243cf6580588055e8aa56320744ba316df6e3a49ca69fd19ec8211300c"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|||||||
7
build.sh
Normal file
7
build.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
## The folloiwng commands should be executed separately.
|
||||||
|
|
||||||
|
docker build -t vwxyzjn/portwarden-scheduler:0.0.1 .
|
||||||
|
|
||||||
|
# minikube docker-env
|
||||||
|
|
||||||
|
docker-compose up
|
||||||
28
core.go
28
core.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -50,6 +51,15 @@ type LoginCredentials struct {
|
|||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateBackupBytesUsingBitwardenLocalJSON(dataJson []byte, BITWARDENCLI_APPDATA_DIR, passphrase, sessionKey string, sleepMilliseconds int) ([]byte, error) {
|
||||||
|
// Put data.json in the BITWARDENCLI_APPDATA_DIR
|
||||||
|
defer BWLogout()
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(BITWARDENCLI_APPDATA_DIR, "data.json"), dataJson, 0644); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return CreateBackupBytes(passphrase, sessionKey, sleepMilliseconds)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateBackupFile(fileName, passphrase, sessionKey string, sleepMilliseconds int) error {
|
func CreateBackupFile(fileName, passphrase, sessionKey string, sleepMilliseconds int) error {
|
||||||
defer BWLogout()
|
defer BWLogout()
|
||||||
if !strings.HasSuffix(fileName, ".portwarden") {
|
if !strings.HasSuffix(fileName, ".portwarden") {
|
||||||
@@ -195,6 +205,24 @@ func BWLoginGetSessionKey(lc *LoginCredentials) (string, error) {
|
|||||||
return sessionKey, nil
|
return sessionKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BWLoginGetSessionKeyAndDataJSON(lc *LoginCredentials, BITWARDENCLI_APPDATA_DIR string) (string, []byte, error) {
|
||||||
|
defer BWLogout()
|
||||||
|
sessionKey, err := BWLoginGetSessionKey(lc)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
dataJSONPath := filepath.Join(BITWARDENCLI_APPDATA_DIR, "data.json")
|
||||||
|
dat, err := ioutil.ReadFile(dataJSONPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
err = os.Remove(dataJSONPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return sessionKey, dat, nil
|
||||||
|
}
|
||||||
|
|
||||||
func BWLogout() error {
|
func BWLogout() error {
|
||||||
cmd := exec.Command("bw", "logout")
|
cmd := exec.Command("bw", "logout")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
|
|||||||
51
docker-compose.yaml
Normal file
51
docker-compose.yaml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
scheduler:
|
||||||
|
image: vwxyzjn/portwarden-base:1.1.0
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- BITWARDENCLI_APPDATA_DIR=/BitwardenCLI
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
volumes:
|
||||||
|
- .:/go/src/github.com/vwxyzjn/portwarden
|
||||||
|
working_dir:
|
||||||
|
/go/src/github.com/vwxyzjn/portwarden/web/scheduler
|
||||||
|
# command:
|
||||||
|
# go run main.go
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
|
||||||
|
worker:
|
||||||
|
image: vwxyzjn/portwarden-base:1.1.0
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- BITWARDENCLI_APPDATA_DIR=/BitwardenCLI
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
deploy:
|
||||||
|
mode: replicated
|
||||||
|
replicas: 2
|
||||||
|
volumes:
|
||||||
|
- .:/go/src/github.com/vwxyzjn/portwarden
|
||||||
|
working_dir:
|
||||||
|
/go/src/github.com/vwxyzjn/portwarden/web/worker
|
||||||
|
command:
|
||||||
|
go run main.go
|
||||||
|
|
||||||
|
redis-commander:
|
||||||
|
image: rediscommander/redis-commander:latest
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- REDIS_HOSTS=local:redis:6379
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
53
k8s/docker-compose.build.yaml
Normal file
53
k8s/docker-compose.build.yaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
scheduler:
|
||||||
|
image: vwxyzjn/portwarden-base:1.6.0
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- BITWARDENCLI_APPDATA_DIR=/BitwardenCLI
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
working_dir:
|
||||||
|
/go/src/github.com/vwxyzjn/portwarden/web/scheduler
|
||||||
|
command:
|
||||||
|
./scheduler
|
||||||
|
labels:
|
||||||
|
kompose.service.expose: "true"
|
||||||
|
kompose.service.type: "loadbalancer"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
|
||||||
|
worker:
|
||||||
|
image: vwxyzjn/portwarden-base:1.6.0
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- BITWARDENCLI_APPDATA_DIR=/BitwardenCLI
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
deploy:
|
||||||
|
mode: replicated
|
||||||
|
replicas: 2
|
||||||
|
working_dir:
|
||||||
|
/go/src/github.com/vwxyzjn/portwarden/web/worker
|
||||||
|
command:
|
||||||
|
./worker
|
||||||
|
|
||||||
|
redis-commander:
|
||||||
|
image: rediscommander/redis-commander:latest
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- REDIS_HOSTS=local:redis:6379
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
|
labels:
|
||||||
|
kompose.service.expose: "true"
|
||||||
|
kompose.service.type: "loadbalancer"
|
||||||
32
k8s/kompose/redis-commander-deployment.yaml
Normal file
32
k8s/kompose/redis-commander-deployment.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.service.expose: "true"
|
||||||
|
kompose.service.type: loadbalancer
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis-commander
|
||||||
|
name: redis-commander
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis-commander
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: REDIS_HOSTS
|
||||||
|
value: local:redis:6379
|
||||||
|
image: rediscommander/redis-commander:latest
|
||||||
|
name: redis-commander
|
||||||
|
ports:
|
||||||
|
- containerPort: 8081
|
||||||
|
resources: {}
|
||||||
|
restartPolicy: Always
|
||||||
|
status: {}
|
||||||
16
k8s/kompose/redis-commander-ingress.yaml
Normal file
16
k8s/kompose/redis-commander-ingress.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis-commander
|
||||||
|
name: redis-commander
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: redis-commander
|
||||||
|
servicePort: 8081
|
||||||
|
status:
|
||||||
|
loadBalancer: {}
|
||||||
22
k8s/kompose/redis-commander-service.yaml
Normal file
22
k8s/kompose/redis-commander-service.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.service.expose: "true"
|
||||||
|
kompose.service.type: loadbalancer
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis-commander
|
||||||
|
name: redis-commander
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: "8081"
|
||||||
|
port: 8081
|
||||||
|
targetPort: 8081
|
||||||
|
selector:
|
||||||
|
io.kompose.service: redis-commander
|
||||||
|
type: LoadBalancer
|
||||||
|
status:
|
||||||
|
loadBalancer: {}
|
||||||
27
k8s/kompose/redis-deployment.yaml
Normal file
27
k8s/kompose/redis-deployment.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis
|
||||||
|
name: redis
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: redis
|
||||||
|
name: redis
|
||||||
|
ports:
|
||||||
|
- containerPort: 6379
|
||||||
|
resources: {}
|
||||||
|
restartPolicy: Always
|
||||||
|
status: {}
|
||||||
19
k8s/kompose/redis-service.yaml
Normal file
19
k8s/kompose/redis-service.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: redis
|
||||||
|
name: redis
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: "6379"
|
||||||
|
port: 6379
|
||||||
|
targetPort: 6379
|
||||||
|
selector:
|
||||||
|
io.kompose.service: redis
|
||||||
|
status:
|
||||||
|
loadBalancer: {}
|
||||||
37
k8s/kompose/scheduler-deployment.yaml
Normal file
37
k8s/kompose/scheduler-deployment.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.service.expose: "true"
|
||||||
|
kompose.service.type: loadbalancer
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: scheduler
|
||||||
|
name: scheduler
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: scheduler
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- ./scheduler
|
||||||
|
env:
|
||||||
|
- name: BITWARDENCLI_APPDATA_DIR
|
||||||
|
value: /BitwardenCLI
|
||||||
|
image: vwxyzjn/portwarden-base:1.6.0
|
||||||
|
name: scheduler
|
||||||
|
ports:
|
||||||
|
- containerPort: 5000
|
||||||
|
resources: {}
|
||||||
|
stdin: true
|
||||||
|
tty: true
|
||||||
|
workingDir: /go/src/github.com/vwxyzjn/portwarden/web/scheduler
|
||||||
|
restartPolicy: Always
|
||||||
|
status: {}
|
||||||
16
k8s/kompose/scheduler-ingress.yaml
Normal file
16
k8s/kompose/scheduler-ingress.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: scheduler
|
||||||
|
name: scheduler
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: scheduler
|
||||||
|
servicePort: 5000
|
||||||
|
status:
|
||||||
|
loadBalancer: {}
|
||||||
23
k8s/kompose/scheduler-service.yaml
Normal file
23
k8s/kompose/scheduler-service.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.service.expose: "true"
|
||||||
|
kompose.service.type: loadbalancer
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: scheduler
|
||||||
|
name: scheduler
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: "5000"
|
||||||
|
port: 5000
|
||||||
|
targetPort: 5000
|
||||||
|
nodePort: 32222
|
||||||
|
selector:
|
||||||
|
io.kompose.service: scheduler
|
||||||
|
type: LoadBalancer
|
||||||
|
status:
|
||||||
|
loadBalancer: {}
|
||||||
33
k8s/kompose/worker-deployment.yaml
Normal file
33
k8s/kompose/worker-deployment.yaml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kompose.cmd: C:\Go\bin\kompose.exe convert -f docker-compose.build.yaml
|
||||||
|
kompose.version: 1.17.0 (a74acad)
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: worker
|
||||||
|
name: worker
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
strategy: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
io.kompose.service: worker
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- ./worker
|
||||||
|
env:
|
||||||
|
- name: BITWARDENCLI_APPDATA_DIR
|
||||||
|
value: /BitwardenCLI
|
||||||
|
image: vwxyzjn/portwarden-base:1.6.0
|
||||||
|
name: worker
|
||||||
|
resources: {}
|
||||||
|
stdin: true
|
||||||
|
tty: true
|
||||||
|
workingDir: /go/src/github.com/vwxyzjn/portwarden/web/worker
|
||||||
|
restartPolicy: Always
|
||||||
|
status: {}
|
||||||
75
web/common.go
Normal file
75
web/common.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
machinery "github.com/RichardKnop/machinery/v1"
|
||||||
|
"github.com/RichardKnop/machinery/v1/config"
|
||||||
|
"github.com/go-redis/redis"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
drive "google.golang.org/api/drive/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackupDefaultSleepMilliseconds = 300
|
||||||
|
PortwardenGoogleDriveBackupFolderName = "portwarden_backup"
|
||||||
|
MachineryRetryCount = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
GoogleDriveAppConfig *oauth2.Config
|
||||||
|
RedisClient *redis.Client
|
||||||
|
MachineryServer *machinery.Server
|
||||||
|
BITWARDENCLI_APPDATA_DIR string
|
||||||
|
GlobalMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitCommonVars() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Setup Redis
|
||||||
|
RedisClient = redis.NewClient(&redis.Options{
|
||||||
|
Addr: "redis:6379",
|
||||||
|
Password: "", // no password set
|
||||||
|
DB: 0, // use default DB
|
||||||
|
})
|
||||||
|
_, err = RedisClient.Ping().Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Machinery
|
||||||
|
var cnf = &config.Config{
|
||||||
|
Broker: "redis://redis:6379/",
|
||||||
|
DefaultQueue: "machinery_tasks",
|
||||||
|
ResultBackend: "redis://redis:6379/",
|
||||||
|
AMQP: &config.AMQPConfig{
|
||||||
|
Exchange: "machinery_exchange",
|
||||||
|
ExchangeType: "direct",
|
||||||
|
BindingKey: "machinery_task",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
MachineryServer, err = machinery.NewServer(cnf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Google things
|
||||||
|
absPath, err := filepath.Abs("../portwardenCredentials.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
credential, err := ioutil.ReadFile(absPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to read client secret file: %v", err)
|
||||||
|
}
|
||||||
|
GoogleDriveAppConfig, err = google.ConfigFromJSON(credential, "https://www.googleapis.com/auth/userinfo.profile", "email", drive.DriveScope)
|
||||||
|
|
||||||
|
// Get Bitwarden CLI Env Var
|
||||||
|
BITWARDENCLI_APPDATA_DIR = os.Getenv("BITWARDENCLI_APPDATA_DIR")
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"installed":{"client_id":"266239424585-t42ok0rq3f9t3plllutq5vrcg7e5p9i5.apps.googleusercontent.com","project_id":"portwarden","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://www.googleapis.com/oauth2/v3/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"uV2tgocN2ha98uwC5Iv5uNiT","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Melody example: chatting</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
#chat {
|
|
||||||
text-align: left;
|
|
||||||
background: #f1f1f1;
|
|
||||||
width: 500px;
|
|
||||||
min-height: 300px;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<center>
|
|
||||||
<h3>Chat</h3>
|
|
||||||
<pre id="chat"></pre>
|
|
||||||
<input placeholder="say something" id="text" type="text">
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var url = "ws://" + window.location.host + "/ws";
|
|
||||||
var ws = new WebSocket(url);
|
|
||||||
var name = "Guest" + Math.floor(Math.random() * 1000);
|
|
||||||
var chat = document.getElementById("chat");
|
|
||||||
var text = document.getElementById("text");
|
|
||||||
var now = function () {
|
|
||||||
var iso = new Date().toISOString();
|
|
||||||
return iso.split("T")[1].split(".")[0];
|
|
||||||
};
|
|
||||||
ws.onmessage = function (msg) {
|
|
||||||
var line = now() + " " + msg.data + "\n";
|
|
||||||
chat.innerText += line;
|
|
||||||
};
|
|
||||||
ws.onopen = function (event) {
|
|
||||||
console.log(event);
|
|
||||||
};
|
|
||||||
text.onkeydown = function (e) {
|
|
||||||
if (e.keyCode === 13 && text.value !== "") {
|
|
||||||
ws.send("<" + name + "> " + text.value);
|
|
||||||
text.value = "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
20
web/main.go
20
web/main.go
@@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/vwxyzjn/portwarden/web/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
credential, err := ioutil.ReadFile("portwardenCredentials.json")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to read client secret file: %v", err)
|
|
||||||
}
|
|
||||||
ps := server.PortwardenServer{
|
|
||||||
Port: 5000,
|
|
||||||
GoogleDriveAppCredentials: credential,
|
|
||||||
}
|
|
||||||
ps.Run()
|
|
||||||
}
|
|
||||||
14
web/scheduler/main.go
Normal file
14
web/scheduler/main.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vwxyzjn/portwarden/web"
|
||||||
|
"github.com/vwxyzjn/portwarden/web/scheduler/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
web.InitCommonVars()
|
||||||
|
ps := server.PortwardenServer{
|
||||||
|
Port: 5000,
|
||||||
|
}
|
||||||
|
ps.Run()
|
||||||
|
}
|
||||||
136
web/scheduler/server/controller.go
Normal file
136
web/scheduler/server/controller.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-redis/redis"
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
"github.com/vwxyzjn/portwarden/web"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrBindingFromGin = "(debugging message) error binding json"
|
||||||
|
ErrRetrievingOauthCode = "error retrieving oauth login credentials; try again"
|
||||||
|
ErrCreatingPortwardenUser = "error creating a portwarden user"
|
||||||
|
ErrGettingPortwardenUser = "error getting a portwarden user"
|
||||||
|
ErrLoginWithBitwarden = "error logging in with Bitwarden"
|
||||||
|
ErrSettingupBackup = "error setting up backup"
|
||||||
|
ErrBackupNotCancelled = "error cancelling back up"
|
||||||
|
|
||||||
|
MsgSuccessfullyCancelledBackingUp = "successfully cancelled backup process"
|
||||||
|
|
||||||
|
FrontEndBaseAddressTest = "http://localhost:8000/"
|
||||||
|
FrontEndBaseAddressProd = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func EncryptBackupHandler(c *gin.Context) {
|
||||||
|
var pu PortwardenUser
|
||||||
|
var opu PortwardenUser
|
||||||
|
if err := c.ShouldBindJSON(&pu); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrBindingFromGin})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opu.Email = pu.Email
|
||||||
|
if err := opu.Get(); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrGettingPortwardenUser})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mergo.Merge(&pu, opu)
|
||||||
|
if err := pu.LoginWithBitwarden(); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrLoginWithBitwarden})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := pu.SetupAutomaticBackup(nil); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": MsgSuccessfullyCancelledBackingUp})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := pu.Set(); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrCreatingPortwardenUser})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CancelEncryptBackupHandler(c *gin.Context) {
|
||||||
|
var pu PortwardenUser
|
||||||
|
var opu PortwardenUser
|
||||||
|
if err := c.ShouldBindJSON(&pu); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrBindingFromGin})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pu.BackupSetting.WillSetupBackup {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "", "message": ErrBackupNotCancelled})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opu.Email = pu.Email
|
||||||
|
if err := opu.Get(); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrGettingPortwardenUser})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opu.BackupSetting = pu.BackupSetting
|
||||||
|
if err := opu.Set(); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrCreatingPortwardenUser})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": MsgSuccessfullyCancelledBackingUp})
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: GoogleDriveHandler() will return Json with the google login url
|
||||||
|
// Not sure if it's supposed to call UploadFile() directly
|
||||||
|
func (ps *PortwardenServer) GetGoogleDriveLoginURLHandler(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"login_url": web.GoogleDriveAppConfig.AuthCodeURL("state-token", oauth2.AccessTypeOffline, oauth2.ApprovalForce),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PortwardenServer) GetGoogleDriveLoginHandler(c *gin.Context) {
|
||||||
|
var gdc GoogleDriveCredentials
|
||||||
|
if err := c.ShouldBind(&gdc); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrRetrievingOauthCode})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tok, err := web.GoogleDriveAppConfig.Exchange(oauth2.NoContext, gdc.Code)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "message": "Login failure"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gui, err := RetrieveUserEmail(tok)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "message": "Login failure"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pu := &PortwardenUser{Email: gui.Email, GoogleToken: tok}
|
||||||
|
err = pu.Get()
|
||||||
|
if err != nil {
|
||||||
|
if err != redis.Nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrGettingPortwardenUser})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create a user
|
||||||
|
err = pu.CreateWithGoogle()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrCreatingPortwardenUser})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Redirect(http.StatusMovedPermanently, FrontEndBaseAddressTest+"home/"+"?access_token="+pu.GoogleToken.AccessToken+"&email="+pu.Email+"&will_setup_backup="+strconv.FormatBool(false))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Using info from exisiting user
|
||||||
|
c.Redirect(http.StatusMovedPermanently, FrontEndBaseAddressTest+"home/"+"?access_token="+tok.AccessToken+"&email="+pu.Email+"&will_setup_backup="+strconv.FormatBool(pu.BackupSetting.WillSetupBackup))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecryptBackupHandler(c *gin.Context) {
|
||||||
|
var dbi DecryptBackupInfo
|
||||||
|
var err error
|
||||||
|
if err = c.ShouldBind(&dbi); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ""})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dbi.File, err = c.FormFile("file"); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ""})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"time"
|
||||||
"strings"
|
|
||||||
|
"github.com/vwxyzjn/portwarden/web"
|
||||||
|
|
||||||
"github.com/antonholmquist/jason"
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
drive "google.golang.org/api/drive/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetClient uses a Context and Config to retrieve a Token
|
// GetClient uses a Context and Config to retrieve a Token
|
||||||
@@ -91,95 +91,65 @@ func SaveToken(file string, token *oauth2.Token) {
|
|||||||
json.NewEncoder(f).Encode(token)
|
json.NewEncoder(f).Encode(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func randStr(strSize int, randType string) string {
|
// UploadFile upload the fileBytes to Google Drive's portwarden folder
|
||||||
|
// https://gist.github.com/tzmartin/f5732091783752660b671c20479f519a
|
||||||
|
func UploadFile(fileBytes []byte, token *oauth2.Token) (*oauth2.Token, error) {
|
||||||
|
// Get updated access token
|
||||||
|
tokenSource := web.GoogleDriveAppConfig.TokenSource(oauth2.NoContext, token)
|
||||||
|
newToken, err := tokenSource.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := web.GoogleDriveAppConfig.Client(oauth2.NoContext, newToken)
|
||||||
|
srv, err := drive.New(client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mimeType := http.DetectContentType(fileBytes)
|
||||||
|
|
||||||
var dictionary string
|
parentId, err := GetOrCreateFolder(srv, web.PortwardenGoogleDriveBackupFolderName)
|
||||||
|
if err != nil {
|
||||||
if randType == "alphanum" {
|
return nil, err
|
||||||
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if randType == "alpha" {
|
f := &drive.File{Title: time.Now().Format("01-02-2006") + ".portwarden", MimeType: mimeType}
|
||||||
dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
if parentId != "" {
|
||||||
|
p := &drive.ParentReference{Id: parentId}
|
||||||
|
f.Parents = []*drive.ParentReference{p}
|
||||||
}
|
}
|
||||||
|
|
||||||
if randType == "number" {
|
_, err = srv.Files.Insert(f).Media(bytes.NewReader(fileBytes)).Do()
|
||||||
dictionary = "0123456789"
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes = make([]byte, strSize)
|
return newToken, nil
|
||||||
rand.Read(bytes)
|
|
||||||
for k, v := range bytes {
|
|
||||||
bytes[k] = dictionary[v%byte(len(dictionary))]
|
|
||||||
}
|
|
||||||
return string(bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multipart upload method
|
func GetOrCreateFolder(srv *drive.Service, folderName string) (string, error) {
|
||||||
// See https://developers.google.com/drive/v3/web/manage-uploads
|
folderId := ""
|
||||||
// uploadFile() takes in the encrypted byte array to be uploaded, client generated by GetClient() and oauth2 token
|
if folderName == "" {
|
||||||
func UploadFile(fileBytes []byte, client *http.Client, token *oauth2.Token) error {
|
return "", nil
|
||||||
|
|
||||||
fileMIMEType := http.DetectContentType(fileBytes)
|
|
||||||
|
|
||||||
postURL := "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"
|
|
||||||
|
|
||||||
// Extract auth or access token from Token file
|
|
||||||
// See https://godoc.org/gnolang.org/x/oauth2#Token
|
|
||||||
authToken := token.AccessToken
|
|
||||||
|
|
||||||
boundary := randStr(32, "alphanum")
|
|
||||||
|
|
||||||
uploadData := []byte("\n" +
|
|
||||||
"--" + boundary + "\n" +
|
|
||||||
"Content-Type: application/json; charset=" + string('"') + "UTF-8" + string('"') + "\n\n" +
|
|
||||||
"{ \n" +
|
|
||||||
string('"') + "name" + string('"') + ":" + string('"') + "test" + string('"') + "\n" +
|
|
||||||
"} \n\n" +
|
|
||||||
"--" + boundary + "\n" +
|
|
||||||
"Content-Type:" + fileMIMEType + "\n\n" +
|
|
||||||
string(fileBytes) + "\n" +
|
|
||||||
|
|
||||||
"--" + boundary + "--")
|
|
||||||
|
|
||||||
// Post to Drive with RESTful method
|
|
||||||
request, err := http.NewRequest("POST", postURL, strings.NewReader(string(uploadData)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
request.Header.Add("Host", "www.googleapis.com")
|
q := fmt.Sprintf("title=\"%s\" and mimeType=\"application/vnd.google-apps.folder\"", folderName)
|
||||||
request.Header.Add("Authorization", "Bearer "+authToken)
|
|
||||||
request.Header.Add("Content-Type", "multipart/related; boundary="+string('"')+boundary+string('"'))
|
|
||||||
request.Header.Add("Content-Length", strconv.FormatInt(request.ContentLength, 10))
|
|
||||||
|
|
||||||
// For debugging
|
r, err := srv.Files.List().Q(q).MaxResults(1).Do()
|
||||||
//fmt.Println(request)
|
|
||||||
response, err := client.Do(request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
if len(r.Items) > 0 {
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
folderId = r.Items[0].Id
|
||||||
if err != nil {
|
} else {
|
||||||
return err
|
// no folder found create new
|
||||||
|
f := &drive.File{Title: folderName, Description: "Auto Create by gdrive-upload", MimeType: "application/vnd.google-apps.folder"}
|
||||||
|
r, err := srv.Files.Insert(f).Do()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
folderId = r.Id
|
||||||
}
|
}
|
||||||
|
return folderId, nil
|
||||||
// Output the response from Drive API
|
|
||||||
fmt.Println(string(body))
|
|
||||||
|
|
||||||
// Extract the uploaded file ID to execute further customization like update the file
|
|
||||||
jsonAPIreply, err := jason.NewObjectFromBytes(body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadedFileID, err := jsonAPIreply.GetString("id")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("Uploaded file ID : ", uploadedFileID)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
50
web/scheduler/server/middleware.go
Normal file
50
web/scheduler/server/middleware.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GoogleOauth2TokenContextVariableName = "GoogleOauth2TokenContextVariableName"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TokenAuthMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var token string
|
||||||
|
HeaderAuthorization, ok := c.Request.Header["Authorization"]
|
||||||
|
if ok && len(HeaderAuthorization) >= 1 {
|
||||||
|
token = HeaderAuthorization[0]
|
||||||
|
token = strings.TrimPrefix(token, "Bearer ")
|
||||||
|
}
|
||||||
|
|
||||||
|
verified, err := VerifyGoogleAccessToekn(token)
|
||||||
|
if err != nil || !verified {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "message": "Verification status is " + strconv.FormatBool(verified)})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Set(GoogleOauth2TokenContextVariableName, token)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CORSMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||||
|
c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
c.AbortWithStatus(200)
|
||||||
|
} else {
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
189
web/scheduler/server/model.go
Normal file
189
web/scheduler/server/model.go
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/RichardKnop/machinery/v1/tasks"
|
||||||
|
"github.com/vwxyzjn/portwarden"
|
||||||
|
"github.com/vwxyzjn/portwarden/web"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrWillNotSetupBackupByUser = "err the user stopped backing up"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BackupSetting struct {
|
||||||
|
Passphrase string `json:"passphrase"`
|
||||||
|
BackupFrequencySeconds int `json:"backup_frequency_seconds"`
|
||||||
|
WillSetupBackup bool `json:"will_setup_backup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DecryptBackupInfo struct {
|
||||||
|
File *multipart.FileHeader `form:"file"`
|
||||||
|
Passphrase string `form:"passphrase"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoogleTokenVerifyResponse struct {
|
||||||
|
IssuedTo string `json:"issued_to"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
VerifiedEmail bool `json:"verified_email"`
|
||||||
|
AccessType string `json:"access_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoogleDriveCredentials struct {
|
||||||
|
State string `form:"state"`
|
||||||
|
Code string `form:"code"`
|
||||||
|
Scope string `form:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortwardenUser struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
BitwardenDataJSON []byte `json:"bitwarden_data_json"`
|
||||||
|
BitwardenSessionKey string `json:"bitwarden_session_key"`
|
||||||
|
BackupSetting BackupSetting `json:"backup_setting"`
|
||||||
|
BitwardenLoginCredentials *portwarden.LoginCredentials `json:"bitwarden_login_credentials"` // Not stored in Redis
|
||||||
|
GoogleUserInfo GoogleUserInfo
|
||||||
|
GoogleToken *oauth2.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoogleUserInfo struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
GivenName string `json:"given_name"`
|
||||||
|
FamilyName string `json:"family_name"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Locale string `json:"locale"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pu *PortwardenUser) CreateWithGoogle() error {
|
||||||
|
var err error
|
||||||
|
pu.GoogleUserInfo, err = RetrieveUserEmail(pu.GoogleToken)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pu.Email = pu.GoogleUserInfo.Email
|
||||||
|
err = pu.Set()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pu *PortwardenUser) LoginWithBitwarden() error {
|
||||||
|
web.GlobalMutex.Lock()
|
||||||
|
defer web.GlobalMutex.Unlock()
|
||||||
|
var err error
|
||||||
|
pu.BitwardenSessionKey, pu.BitwardenDataJSON, err = portwarden.BWLoginGetSessionKeyAndDataJSON(pu.BitwardenLoginCredentials, web.BITWARDENCLI_APPDATA_DIR)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pu *PortwardenUser) SetupAutomaticBackup(eta *time.Time) error {
|
||||||
|
if !pu.BackupSetting.WillSetupBackup {
|
||||||
|
return errors.New(ErrWillNotSetupBackupByUser)
|
||||||
|
}
|
||||||
|
signature := &tasks.Signature{
|
||||||
|
Name: "BackupToGoogleDrive",
|
||||||
|
Args: []tasks.Arg{
|
||||||
|
{
|
||||||
|
Type: "string",
|
||||||
|
Value: pu.Email,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ETA: eta,
|
||||||
|
RetryCount: web.MachineryRetryCount,
|
||||||
|
}
|
||||||
|
_, err := web.MachineryServer.SendTask(signature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pu *PortwardenUser) Set() error {
|
||||||
|
pu.BitwardenLoginCredentials = &portwarden.LoginCredentials{}
|
||||||
|
puJson, err := json.Marshal(pu)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = web.RedisClient.Set(pu.Email, string(puJson), 0).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pu *PortwardenUser) Get() error {
|
||||||
|
val, err := web.RedisClient.Get(pu.Email).Result()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(val), &pu); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyGoogleAccessToekn(access_token string) (bool, error) {
|
||||||
|
url := "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + access_token
|
||||||
|
response, err := http.Get(url)
|
||||||
|
defer response.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
var gtvr GoogleTokenVerifyResponse
|
||||||
|
if err := json.Unmarshal(body, >vr); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !gtvr.VerifiedEmail {
|
||||||
|
return false, errors.New(string(body))
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RetrieveUserEmail(token *oauth2.Token) (GoogleUserInfo, error) {
|
||||||
|
var gui GoogleUserInfo
|
||||||
|
postURL := "https://www.googleapis.com/oauth2/v2/userinfo"
|
||||||
|
request, err := http.NewRequest("GET", postURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return gui, err
|
||||||
|
}
|
||||||
|
request.Header.Add("Host", "www.googleapis.com")
|
||||||
|
request.Header.Add("Authorization", "Bearer "+token.AccessToken)
|
||||||
|
request.Header.Add("Content-Length", strconv.FormatInt(request.ContentLength, 10))
|
||||||
|
|
||||||
|
GoogleDriveClient := web.GoogleDriveAppConfig.Client(oauth2.NoContext, token)
|
||||||
|
response, err := GoogleDriveClient.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return gui, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return gui, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &gui); err != nil {
|
||||||
|
return gui, err
|
||||||
|
}
|
||||||
|
return gui, nil
|
||||||
|
}
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
|
||||||
drive "google.golang.org/api/drive/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PortwardenServer struct {
|
type PortwardenServer struct {
|
||||||
@@ -19,29 +16,28 @@ type PortwardenServer struct {
|
|||||||
GoogleDriveContext context.Context
|
GoogleDriveContext context.Context
|
||||||
GoogleDriveAppCredentials []byte
|
GoogleDriveAppCredentials []byte
|
||||||
GoogleDriveAppConfig *oauth2.Config
|
GoogleDriveAppConfig *oauth2.Config
|
||||||
GoogleClient *http.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PortwardenServer) Run() {
|
func (ps *PortwardenServer) Run() {
|
||||||
var err error
|
spew.Dump("Scheduler Server Started")
|
||||||
ps.GoogleDriveContext = context.Background()
|
|
||||||
ps.GoogleDriveAppConfig, err = google.ConfigFromJSON(ps.GoogleDriveAppCredentials, drive.DriveScope)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
|
||||||
}
|
|
||||||
// ps.GoogleClient = GetClient(ps.GoogleDriveContext, ps.GoogleDriveAppConfig)
|
|
||||||
|
|
||||||
ps.Router = gin.Default()
|
ps.Router = gin.Default()
|
||||||
ps.Router.Use(cors.Default())
|
ps.Router.Use(CORSMiddleware())
|
||||||
|
|
||||||
ps.Router.GET("/", func(c *gin.Context) {
|
ps.Router.GET("/", func(c *gin.Context) {
|
||||||
http.ServeFile(c.Writer, c.Request, "index.html")
|
c.JSON(200, "Welcome to Portwarden API")
|
||||||
})
|
})
|
||||||
|
|
||||||
ps.Router.POST("/encrypt", EncryptBackupHandler)
|
|
||||||
ps.Router.POST("/decrypt", DecryptBackupHandler)
|
ps.Router.POST("/decrypt", DecryptBackupHandler)
|
||||||
ps.Router.GET("/gdrive/loginUrl", ps.GetGoogleDriveLoginURLHandler)
|
ps.Router.GET("/gdrive/loginUrl", ps.GetGoogleDriveLoginURLHandler)
|
||||||
|
|
||||||
ps.Router.GET("/gdrive/login", ps.GetGoogleDriveLoginHandler)
|
ps.Router.GET("/gdrive/login", ps.GetGoogleDriveLoginHandler)
|
||||||
|
|
||||||
|
ps.Router.Use(TokenAuthMiddleware())
|
||||||
|
ps.Router.GET("/test/TokenAuthMiddleware", func(c *gin.Context) {
|
||||||
|
c.JSON(200, "success")
|
||||||
|
})
|
||||||
|
ps.Router.POST("/encrypt", EncryptBackupHandler)
|
||||||
|
ps.Router.POST("/encrypt/cancel", CancelEncryptBackupHandler)
|
||||||
|
|
||||||
ps.Router.Run(":" + strconv.Itoa(ps.Port))
|
ps.Router.Run(":" + strconv.Itoa(ps.Port))
|
||||||
}
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mime/multipart"
|
|
||||||
|
|
||||||
"github.com/vwxyzjn/portwarden"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
BackupDefaultSleepMilliseconds = 300
|
|
||||||
)
|
|
||||||
|
|
||||||
type EncryptBackupInfo struct {
|
|
||||||
FileNamePrefix string `json:"filename_prefix"`
|
|
||||||
Passphrase string `json:"passphrase"`
|
|
||||||
BitwardenLoginCredentials portwarden.LoginCredentials `json:"bitwarden_login_credentials"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DecryptBackupInfo struct {
|
|
||||||
File *multipart.FileHeader `form:"file"`
|
|
||||||
Passphrase string `form:"passphrase"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GoogleDriveCredentials struct {
|
|
||||||
State string `form:"state"`
|
|
||||||
Code string `form:"code"`
|
|
||||||
Scope string `form:"scope"`
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/vwxyzjn/portwarden"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrRetrievingOauthCode = "error retrieving oauth login credentials; try again"
|
|
||||||
)
|
|
||||||
|
|
||||||
func EncryptBackupHandler(c *gin.Context) {
|
|
||||||
var ebi EncryptBackupInfo
|
|
||||||
if err := c.ShouldBindJSON(&ebi); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ""})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sessionKey, err := portwarden.BWLoginGetSessionKey(&ebi.BitwardenLoginCredentials)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": sessionKey})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = portwarden.CreateBackupFile(ebi.FileNamePrefix, ebi.Passphrase, sessionKey, BackupDefaultSleepMilliseconds)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": sessionKey})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: GoogleDriveHandler() will return Json with the google login url
|
|
||||||
// Not sure if it's supposed to call UploadFile() directly
|
|
||||||
func (ps *PortwardenServer) GetGoogleDriveLoginURLHandler(c *gin.Context) {
|
|
||||||
c.JSON(200, gin.H{
|
|
||||||
"login_url": ps.GoogleDriveAppConfig.AuthCodeURL("state-token"),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PortwardenServer) GetGoogleDriveLoginHandler(c *gin.Context) {
|
|
||||||
var gdc GoogleDriveCredentials
|
|
||||||
if err := c.ShouldBind(&gdc); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrRetrievingOauthCode})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tok, err := ps.GoogleDriveAppConfig.Exchange(ps.GoogleDriveContext, gdc.Code)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrRetrievingOauthCode})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
GoogleDriveClient := ps.GoogleDriveAppConfig.Client(ps.GoogleDriveContext, tok)
|
|
||||||
fileBytes := []byte("xixix")
|
|
||||||
err = UploadFile(fileBytes, GoogleDriveClient, tok)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ErrRetrievingOauthCode})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, "Login Successful")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecryptBackupHandler(c *gin.Context) {
|
|
||||||
var dbi DecryptBackupInfo
|
|
||||||
var err error
|
|
||||||
if err = c.ShouldBind(&dbi); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ""})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if dbi.File, err = c.FormFile("file"); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "message": ""})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
62
web/worker/main.go
Normal file
62
web/worker/main.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vwxyzjn/portwarden"
|
||||||
|
"github.com/vwxyzjn/portwarden/web"
|
||||||
|
"github.com/vwxyzjn/portwarden/web/scheduler/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
web.InitCommonVars()
|
||||||
|
web.MachineryServer.RegisterTasks(map[string]interface{}{
|
||||||
|
"BackupToGoogleDrive": BackupToGoogleDrive,
|
||||||
|
})
|
||||||
|
worker := web.MachineryServer.NewWorker("worker_name", 0)
|
||||||
|
err := worker.Launch()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BackupToGoogleDrive(email string) error {
|
||||||
|
web.GlobalMutex.Lock()
|
||||||
|
defer web.GlobalMutex.Unlock()
|
||||||
|
name, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("BackupToGoogleDrive called from worker:", name)
|
||||||
|
pu := server.PortwardenUser{Email: email}
|
||||||
|
err = pu.Get()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
encryptedData, err := portwarden.CreateBackupBytesUsingBitwardenLocalJSON(pu.BitwardenDataJSON, web.BITWARDENCLI_APPDATA_DIR, pu.BackupSetting.Passphrase, pu.BitwardenSessionKey, web.BackupDefaultSleepMilliseconds)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newToken, err := server.UploadFile(encryptedData, pu.GoogleToken)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pu.GoogleToken = newToken
|
||||||
|
eta := time.Now().UTC().Add(time.Second * time.Duration(pu.BackupSetting.BackupFrequencySeconds))
|
||||||
|
err = pu.SetupAutomaticBackup(&eta)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() != server.ErrWillNotSetupBackupByUser {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
fmt.Printf("user %v cancelled backup", pu.Email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the access token
|
||||||
|
err = pu.Set()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user