1
0
mirror of https://github.com/vwxyzjn/portwarden synced 2025-12-10 05:13:54 +00:00

Merge pull request #9 from vwxyzjn/API-6

Api 6
This commit is contained in:
Costa Huang
2018-12-04 00:03:36 -05:00
committed by GitHub
31 changed files with 1335 additions and 281 deletions

2
.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
vendor
Dockerfile

31
Dockerfile Normal file
View 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
View 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
View File

@@ -3,15 +3,107 @@
[[projects]]
name = "cloud.google.com/go"
packages = ["compute/metadata"]
packages = [
"compute/metadata",
"iam",
"internal/optional",
"internal/version",
"pubsub",
"pubsub/apiv1",
"pubsub/internal/distribution"
]
revision = "74b12019e2aa53ec27882158f59192d7cd6d1998"
version = "v0.33.1"
[[projects]]
name = "github.com/antonholmquist/jason"
branch = "master"
name = "github.com/RichardKnop/logging"
packages = ["."]
revision = "c23cef7eaa75a6a5b8810120e167bd590d8fd2ab"
version = "v1.0.0"
revision = "b1d5d44c82d6c9fb6c8e8dad28412163586fd8ac"
[[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]]
name = "github.com/davecgh/go-spew"
@@ -32,12 +124,6 @@
]
revision = "cc9eb1d7ad760af14e8f918698f745e80377af4f"
[[projects]]
name = "github.com/gin-contrib/cors"
packages = ["."]
revision = "cf4846e6a636a76237a28d9286f163c132e841bc"
version = "v1.2"
[[projects]]
branch = "master"
name = "github.com/gin-contrib/sse"
@@ -55,9 +141,32 @@
revision = "b869fe1415e4b9eb52f247441830d502aece2d4d"
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]]
name = "github.com/golang/protobuf"
packages = ["proto"]
packages = [
"proto",
"protoc-gen-go/descriptor",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/empty",
"ptypes/timestamp"
]
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0"
@@ -67,12 +176,50 @@
packages = ["."]
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]]
name = "github.com/json-iterator/go"
packages = ["."]
revision = "1624edc4454b8682399def8740d46db5e4362ba4"
version = "v1.1.5"
[[projects]]
name = "github.com/kelseyhightower/envconfig"
packages = ["."]
revision = "f611eb38b3875cc3bd991ca91c51d06446afa14c"
version = "v1.3.0"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
@@ -103,6 +250,16 @@
revision = "cc3e4b2381762c56dbcdf9f93be03349a1dc1c14"
version = "v1.0.0"
[[projects]]
name = "github.com/opentracing/opentracing-go"
packages = [
".",
"ext",
"log"
]
revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
version = "v1.0.2"
[[projects]]
name = "github.com/pierrec/lz4"
packages = [
@@ -112,6 +269,12 @@
revision = "635575b42742856941dbc767b44905bb9ba083f6"
version = "v2.0.7"
[[projects]]
branch = "master"
name = "github.com/streadway/amqp"
packages = ["."]
revision = "27835f1a64e97101d95306211f03c0620ffa295d"
[[projects]]
branch = "master"
name = "github.com/tidwall/pretty"
@@ -135,6 +298,28 @@
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
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]]
branch = "master"
name = "golang.org/x/crypto"
@@ -146,7 +331,13 @@
name = "golang.org/x/net"
packages = [
"context",
"context/ctxhttp"
"context/ctxhttp",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"trace"
]
revision = "adae6a3d119ae4890b46832a2e88a95adc62b8e7"
@@ -162,21 +353,59 @@
]
revision = "f42d05182288abf10faef86d16c0d07b8d40ea2d"
[[projects]]
branch = "master"
name = "golang.org/x/sync"
packages = [
"errgroup",
"semaphore"
]
revision = "42b317875d0fa942474b76e1b46a6060d720ae6e"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
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]]
branch = "master"
name = "google.golang.org/api"
packages = [
"drive/v2",
"drive/v3",
"gensupport",
"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"
@@ -191,18 +420,78 @@
"internal/log",
"internal/modules",
"internal/remote_api",
"internal/socket",
"internal/urlfetch",
"socket",
"urlfetch"
]
revision = "4a4468ece617fc8205e99368fa2200e9d1fad421"
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]]
name = "gopkg.in/go-playground/validator.v8"
packages = ["."]
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
version = "v8.18.2"
[[projects]]
branch = "v2"
name = "gopkg.in/mgo.v2"
packages = [
".",
"bson",
"internal/json",
"internal/sasl",
"internal/scram"
]
revision = "9856a29383ce1c59f308dd1cf0363a79b5bef6b5"
[[projects]]
name = "gopkg.in/urfave/cli.v1"
packages = ["."]
@@ -218,6 +507,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e94795c347f48a9ee90b68d79ae43762c0504d74ca19276f92b7c394faeab698"
inputs-digest = "b0982d243cf6580588055e8aa56320744ba316df6e3a49ca69fd19ec8211300c"
solver-name = "gps-cdcl"
solver-version = 1

7
build.sh Normal file
View 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
View File

@@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -50,6 +51,15 @@ type LoginCredentials struct {
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 {
defer BWLogout()
if !strings.HasSuffix(fileName, ".portwarden") {
@@ -195,6 +205,24 @@ func BWLoginGetSessionKey(lc *LoginCredentials) (string, error) {
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 {
cmd := exec.Command("bw", "logout")
return cmd.Run()

51
docker-compose.yaml Normal file
View 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"

View 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"

View 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: {}

View 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: {}

View 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: {}

View 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: {}

View 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: {}

View 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: {}

View 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: {}

View 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: {}

View 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
View 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")
}

View File

@@ -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"]}}

View File

@@ -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>

View File

@@ -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
View 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()
}

View 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": ""})
}
}

View File

@@ -1,22 +1,22 @@
package server
import (
"crypto/rand"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"os/user"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/vwxyzjn/portwarden/web"
"github.com/antonholmquist/jason"
"golang.org/x/net/context"
"golang.org/x/oauth2"
drive "google.golang.org/api/drive/v2"
)
// 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)
}
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
if randType == "alphanum" {
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
parentId, err := GetOrCreateFolder(srv, web.PortwardenGoogleDriveBackupFolderName)
if err != nil {
return nil, err
}
if randType == "alpha" {
dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
f := &drive.File{Title: time.Now().Format("01-02-2006") + ".portwarden", MimeType: mimeType}
if parentId != "" {
p := &drive.ParentReference{Id: parentId}
f.Parents = []*drive.ParentReference{p}
}
if randType == "number" {
dictionary = "0123456789"
_, err = srv.Files.Insert(f).Media(bytes.NewReader(fileBytes)).Do()
if err != nil {
return nil, err
}
var bytes = make([]byte, strSize)
rand.Read(bytes)
for k, v := range bytes {
bytes[k] = dictionary[v%byte(len(dictionary))]
}
return string(bytes)
return newToken, nil
}
// Multipart upload method
// See https://developers.google.com/drive/v3/web/manage-uploads
// uploadFile() takes in the encrypted byte array to be uploaded, client generated by GetClient() and oauth2 token
func UploadFile(fileBytes []byte, client *http.Client, token *oauth2.Token) error {
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
func GetOrCreateFolder(srv *drive.Service, folderName string) (string, error) {
folderId := ""
if folderName == "" {
return "", nil
}
request.Header.Add("Host", "www.googleapis.com")
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))
q := fmt.Sprintf("title=\"%s\" and mimeType=\"application/vnd.google-apps.folder\"", folderName)
// For debugging
//fmt.Println(request)
response, err := client.Do(request)
r, err := srv.Files.List().Q(q).MaxResults(1).Do()
if err != nil {
return err
return "", err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if len(r.Items) > 0 {
folderId = r.Items[0].Id
} else {
// 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
return "", err
}
// 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
folderId = r.Id
}
uploadedFileID, err := jsonAPIreply.GetString("id")
if err != nil {
return err
}
fmt.Println("Uploaded file ID : ", uploadedFileID)
return nil
return folderId, nil
}
/*

View 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()
}
}
}

View 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, &gtvr); 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
}

View File

@@ -1,16 +1,13 @@
package server
import (
"log"
"net/http"
"strconv"
"github.com/gin-contrib/cors"
"github.com/davecgh/go-spew/spew"
"github.com/gin-gonic/gin"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
drive "google.golang.org/api/drive/v2"
)
type PortwardenServer struct {
@@ -19,29 +16,28 @@ type PortwardenServer struct {
GoogleDriveContext context.Context
GoogleDriveAppCredentials []byte
GoogleDriveAppConfig *oauth2.Config
GoogleClient *http.Client
}
func (ps *PortwardenServer) Run() {
var err error
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)
spew.Dump("Scheduler Server Started")
ps.Router = gin.Default()
ps.Router.Use(cors.Default())
ps.Router.Use(CORSMiddleware())
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.GET("/gdrive/loginUrl", ps.GetGoogleDriveLoginURLHandler)
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))
}

View File

@@ -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"`
}

View File

@@ -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
View 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
}