Skip to content

Commit

Permalink
Add more login methods
Browse files Browse the repository at this point in the history
tulir committed Jul 27, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 8e660a5 commit 3ae731a
Showing 12 changed files with 524 additions and 59 deletions.
69 changes: 69 additions & 0 deletions .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Build Docker

on:
push:
pull_request:
branches: [main]

env:
BEEPER_BRIDGE_TYPE: dummybridge
CI_REGISTRY_IMAGE: "${{ secrets.CI_REGISTRY }}/bridge/dummybridgego"
GHCR_REGISTRY: ghcr.io
GHCR_REGISTRY_IMAGE: "ghcr.io/${{ github.repository }}/go"
GHCR_REGISTRY_LOGINHELPER_IMAGE: "ghcr.io/${{ github.repository }}/loginhelper"

jobs:
build-docker-dummybridge:
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Beeper Docker registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.CI_REGISTRY }}
username: ${{ secrets.CI_REGISTRY_USER }}
password: ${{ secrets.CI_REGISTRY_PASSWORD }}

- name: Login to ghcr
uses: docker/login-action@v3
with:
registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker Build
uses: docker/build-push-action@v6
with:
# cache-from: ${{ env.CI_REGISTRY_IMAGE }}:latest
pull: true
file: ./cmd/dummybridge/Dockerfile
tags: |
${{ env.CI_REGISTRY_IMAGE }}:${{ github.sha }}
${{ env.GHCR_REGISTRY_IMAGE }}:${{ github.sha }}
push: true

build-docker-loginhelper:
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to ghcr
uses: docker/login-action@v3
with:
registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker Build
uses: docker/build-push-action@v6
with:
cache-from: ${{ env.GHCR_REGISTRY_LOGINHELPER_IMAGE }}/loginhelper:latest
pull: true
file: ./cmd/dummybridge/Dockerfile
tags: |
${{ env.GHCR_REGISTRY_LOGINHELPER_IMAGE }}:${{ github.sha }}
${{ env.GHCR_REGISTRY_LOGINHELPER_IMAGE }}:latest
push: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.db*
*.yaml
*.log
12 changes: 12 additions & 0 deletions cmd/dummybridge/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM golang:1-alpine3.20 AS builder

COPY . /build
WORKDIR /build
ENV CGO_ENABLED=0
RUN go build ./cmd/dummybridge -o /usr/bin/dummybridge

FROM alpine:3.20

RUN apk add --no-cache ca-certificates
COPY --from=builder /usr/bin/dummybridge /usr/bin/dummybridge
CMD ["/usr/bin/dummybridge"]
12 changes: 12 additions & 0 deletions cmd/loginhelper/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM golang:1-alpine3.20 AS builder

COPY . /build
WORKDIR /build
ENV CGO_ENABLED=0
RUN go build ./cmd/loginhelper -o /usr/bin/loginhelper

FROM alpine:3.20

RUN apk add --no-cache ca-certificates
COPY --from=builder /usr/bin/loginhelper /usr/bin/loginhelper
CMD ["/usr/bin/loginhelper"]
117 changes: 117 additions & 0 deletions cmd/loginhelper/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"context"
"embed"
"encoding/json"
"net/http"
"sync"
"time"

"go.mau.fi/util/exerrors"
)

//go:embed pages/*.html
var pages embed.FS

var waiters = make(map[string]chan map[string]string)
var inFlightRequests = make(map[string]int)
var lock sync.RWMutex

func addWaiter(ip, id string, waiter chan map[string]string) bool {
lock.Lock()
defer lock.Unlock()
if inFlightRequests[ip] > 5 {
return false
}
inFlightRequests[ip]++
waiters[id] = waiter
return true
}

func callWaiter(id string, data map[string]string) bool {
lock.RLock()
defer lock.RUnlock()
waiter, ok := waiters[id]
if !ok {
return false
}
waiter <- data
return true
}

type errorResp struct {
Error string `json:"error"`
}

func response(w http.ResponseWriter, status int, resp any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(resp)
}

func postWait(w http.ResponseWriter, r *http.Request) {
ip := r.Header.Get("X-Forwarded-For")
waiter := make(chan map[string]string)
reqID := r.PathValue("id")
if !addWaiter(ip, reqID, waiter) {
response(w, http.StatusTooManyRequests, errorResp{"Too many in-flight requests"})
return
}
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
defer func() {
lock.Lock()
defer lock.Unlock()
inFlightRequests[ip]--
delete(waiters, reqID)
}()
select {
case <-ctx.Done():
response(w, http.StatusTooManyRequests, errorResp{"Wait timeout"})
case resp := <-waiter:
response(w, http.StatusOK, resp)
}
}

func postSubmit(w http.ResponseWriter, r *http.Request) {
reqID := r.PathValue("id")
var input map[string]string
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
response(w, http.StatusBadRequest, errorResp{"Invalid JSON"})
return
}
if callWaiter(reqID, input) {
response(w, http.StatusOK, struct{}{})
} else {
response(w, http.StatusNotFound, errorResp{"Request not found"})
}
}

func postSetCookies(w http.ResponseWriter, r *http.Request) {
var input map[string]string
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
response(w, http.StatusBadRequest, errorResp{"Invalid JSON"})
return
}
for k, v := range input {
http.SetCookie(w, &http.Cookie{
Name: k,
Value: v,
Expires: time.Now().Add(1 * time.Hour),
HttpOnly: true,
Secure: true,
})
}
response(w, http.StatusOK, struct{}{})
}

func main() {
http.Handle("/pages/", http.FileServerFS(pages))
http.HandleFunc("POST /api/daw_wait/{id}", postWait)
http.HandleFunc("POST /api/daw_submit/{id}", postSubmit)
http.HandleFunc("POST /api/set_cookies", postSetCookies)
exerrors.PanicIfNotNil(http.ListenAndServe(":8080", nil))
}
46 changes: 46 additions & 0 deletions cmd/loginhelper/pages/cookies.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dummybridge cookie login</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script>
function saveToCookies() {
fetch("../api/set_cookies", {
method: "POST",
body: JSON.stringify({
username: document.getElementById("username").value,
password: document.getElementById("password").value
}),
headers: {
"Content-Type": "application/json"
},
}).catch(err => alert(err))
}
</script>
<style>
div {
margin-bottom: 8px;
}
input {
padding: 8px;
}
button {
padding: 8px;
}
</style>
</head>
<body>
<div>
<label for="username">Username:</label>
<input type="text" id="username" placeholder="Username"/>
</div>

<div>
<label for="password">Password:</label>
<input type="password" id="password" placeholder="Password"/>
</div>

<button id="login" onClick="saveToCookies()">Save to cookies</button>
</body>
</html>
60 changes: 60 additions & 0 deletions cmd/loginhelper/pages/daw_submit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dummybridge display & wait login</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script>
function normalizeCrockfordBase32(input) {
return input.toUpperCase()
.replace("O", "0")
.replace("I", "1")
.replace("L", "1")
.replace("_", "-")
.replace(/[^0123456789ABCDEFGHJKMNPQRSTVWXYZ-]/, "")
}
function submitToWaiter() {
const reqID = document.getElementById("reqid").value
fetch(`../api/daw_submit/${reqID}`, {
method: "POST",
body: JSON.stringify({
username: document.getElementById("username").value,
password: document.getElementById("password").value
}),
headers: {
"Content-Type": "application/json"
},
}).catch(err => alert(err))
}
</script>
<style>
div {
margin-bottom: 10px;
}
input {
padding: 8px;
}
button {
padding: 8px;
}
</style>
</head>
<body>
<div>
<label for="reqid">Request ID:</label>
<input type="text" id="reqid" oninput="this.value = normalizeCrockfordBase32(this.value)" placeholder="ABC-123-DEF"/>
</div>

<div>
<label for="username">Username:</label>
<input type="text" id="username" placeholder="Username"/>
</div>

<div>
<label for="password">Password:</label>
<input type="password" id="password" placeholder="Password"/>
</div>

<button id="login" onClick="submitToWaiter()">Submit to waiter</button>
</body>
</html>
38 changes: 38 additions & 0 deletions cmd/loginhelper/pages/localstorage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dummybridge localstorage login</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script>
function saveToLocalstorage() {
localStorage.setItem("username", document.getElementById("username").value)
localStorage.setItem("password", document.getElementById("password").value)
}
</script>
<style>
div {
margin-bottom: 10px;
}
input {
padding: 8px;
}
button {
padding: 8px;
}
</style>
</head>
<body>
<div>
<label for="username">Username:</label>
<input type="text" id="username" placeholder="Username"/>
</div>

<div>
<label for="password">Password:</label>
<input type="password" id="password" placeholder="Password"/>
</div>

<button id="login" onClick="saveToLocalstorage()">Save to cookies</button>
</body>
</html>
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module github.com/beeper/dummybridge

go 1.21
go 1.22

require (
go.mau.fi/util v0.5.1-0.20240713134429-03648b3ede41
maunium.net/go/mautrix v0.19.0-beta.1.0.20240714080619-d1905f623215
go.mau.fi/util v0.6.1-0.20240719175439-20a6073e1dd4
maunium.net/go/mautrix v0.19.1-0.20240726171246-426921e00a7c
)

require (
@@ -23,11 +23,11 @@ require (
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/yuin/goldmark v1.7.4 // indirect
go.mau.fi/zeroconfig v0.1.2 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
go.mau.fi/zeroconfig v0.1.3 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
maunium.net/go/mauflag v1.0.0 // indirect
55 changes: 23 additions & 32 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@@ -15,14 +19,16 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -34,43 +40,28 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.mau.fi/util v0.4.2 h1:RR3TOcRHmCF9Bx/3YG4S65MYfa+nV6/rn8qBWW4Mi30=
go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw=
go.mau.fi/util v0.5.1-0.20240702075351-577617730cb7 h1:1avw60QZMpzzMMisf6Jqm+WSycZ59OHJA5IlSXHCCPE=
go.mau.fi/util v0.5.1-0.20240702075351-577617730cb7/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4=
go.mau.fi/util v0.5.1-0.20240713134429-03648b3ede41 h1:suJqVZoWuiqmMo/xojAGSxz04fOYYu0oE7sFPrf2L5c=
go.mau.fi/util v0.5.1-0.20240713134429-03648b3ede41/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4=
go.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto=
go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
go.mau.fi/util v0.6.1-0.20240719175439-20a6073e1dd4 h1:CYKYs5jwJ0bFJqh6pRoWtC9NIJ0lz0/6i2SC4qEBFaU=
go.mau.fi/util v0.6.1-0.20240719175439-20a6073e1dd4/go.mod h1:ljYdq3sPfpICc3zMU+/mHV/sa4z0nKxc67hSBwnrk8U=
go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM=
go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.18.1 h1:a6mUsJixegBNTXUoqC5RQ9gsumIPzKvCubKwF+zmCt4=
maunium.net/go/mautrix v0.18.1/go.mod h1:2oHaq792cSXFGvxLvYw3Gf1L4WVVP4KZcYys5HVk/h8=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240710204602-dd16a8d1d90b h1:xYEIkM0OzirvHPpTwYiDQrh6PHjDny5Ox84SIkD2aXc=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240710204602-dd16a8d1d90b/go.mod h1:bNQrvIftiwJ+7OjSh+Gza5xcncq1ooHk6oyDWq4B4sg=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240714080619-d1905f623215 h1:cy3Ge8DMOQj+a6G8QhWEcg8FWsx2wEwTgSATHzg3wIE=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240714080619-d1905f623215/go.mod h1:ldNVOQXaljMk4YLzlohp+DniMQtCSzTVcwjEFBlYQLM=
maunium.net/go/mautrix v0.19.1-0.20240726171246-426921e00a7c h1:FMS7nXQ1HKcncWaM5/xoM3Cqeakf/BSEcZ04MYun+18=
maunium.net/go/mautrix v0.19.1-0.20240726171246-426921e00a7c/go.mod h1:xP3DCXdPBUe1sPiugLbd5mRh/mJQWfGWyED1S8s9V7c=
14 changes: 13 additions & 1 deletion pkg/connector/connector.go
Original file line number Diff line number Diff line change
@@ -58,9 +58,21 @@ func (dc *DummyConnector) GetLoginFlows() []bridgev2.LoginFlow {
Name: "Password",
Description: "Log in with a password",
ID: "password",
}, {
Name: "Cookies",
Description: "Log in with extracted cookies",
ID: "cookies",
}, {
Name: "Local storage",
Description: "Log in with extracted local storage",
ID: "localstorage",
}, {
Name: "Display and wait",
Description: "Log in through a remote server",
ID: "displayandwait",
}}
}

func (dc *DummyConnector) CreateLogin(ctx context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) {
return &DummyLogin{User: user}, nil
return &DummyLogin{User: user, FlowID: flowID}, nil
}
141 changes: 123 additions & 18 deletions pkg/connector/login.go
Original file line number Diff line number Diff line change
@@ -2,36 +2,141 @@ package connector

import (
"context"
"encoding/base32"
"encoding/json"
"fmt"
"net/http"

"go.mau.fi/util/random"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database"
"maunium.net/go/mautrix/bridgev2/networkid"
)

type DummyLogin struct {
User *bridgev2.User
User *bridgev2.User
FlowID string
DAWID string
}

var crockfordBase32 = base32.NewEncoding("0123456789ABCDEFGHJKMNPQRSTVWXYZ").WithPadding(base32.NoPadding)

func (dl *DummyLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeUserInput,
StepID: "com.beeper.dummy.password",
Instructions: "",
UserInputParams: &bridgev2.LoginUserInputParams{
Fields: []bridgev2.LoginInputDataField{
{
Type: bridgev2.LoginInputFieldTypeUsername,
ID: "username",
Name: "username, anything goes and it's used as the ID",
},
{
Type: bridgev2.LoginInputFieldTypePassword,
ID: "password",
Name: "password, anything goes",
switch dl.FlowID {
case "password":
return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeUserInput,
StepID: "com.beeper.dummy.password",
Instructions: "",
UserInputParams: &bridgev2.LoginUserInputParams{
Fields: []bridgev2.LoginInputDataField{
{
Type: bridgev2.LoginInputFieldTypeUsername,
ID: "username",
Name: "username, anything goes and it's used as the ID",
},
{
Type: bridgev2.LoginInputFieldTypePassword,
ID: "password",
Name: "password, anything goes",
},
},
},
},
}, nil
}, nil
case "cookies":
return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeCookies,
StepID: "com.beeper.dummy.cookies",
Instructions: "",
CookiesParams: &bridgev2.LoginCookiesParams{
URL: "https://random.mau.fi/dummy/page/cookies.html",
Fields: []bridgev2.LoginCookieField{{
ID: "username",
Required: true,
Sources: []bridgev2.LoginCookieFieldSource{{
Type: bridgev2.LoginCookieTypeCookie,
Name: "username",
CookieDomain: "random.mau.fi",
}},
}, {
ID: "password",
Required: true,
Sources: []bridgev2.LoginCookieFieldSource{{
Type: bridgev2.LoginCookieTypeCookie,
Name: "password",
CookieDomain: "random.mau.fi",
}},
}},
},
}, nil
case "localstorage":
return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeCookies,
StepID: "com.beeper.dummy.localstorage",
Instructions: "",
CookiesParams: &bridgev2.LoginCookiesParams{
URL: "https://random.mau.fi/dummy/page/localstorage.html",
Fields: []bridgev2.LoginCookieField{{
ID: "username",
Required: true,
Sources: []bridgev2.LoginCookieFieldSource{{
Type: bridgev2.LoginCookieTypeLocalStorage,
Name: "username",
}},
}, {
ID: "password",
Required: true,
Sources: []bridgev2.LoginCookieFieldSource{{
Type: bridgev2.LoginCookieTypeLocalStorage,
Name: "password",
}},
}},
},
}, nil
case "displayandwait":
dl.DAWID = randomCode()
return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeDisplayAndWait,
StepID: "com.beeper.dummy.displayandwait",
Instructions: "Enter the code on https://random.mau.fi/dummy/page/daw_submit.html",
DisplayAndWaitParams: &bridgev2.LoginDisplayAndWaitParams{
Type: bridgev2.LoginDisplayTypeCode,
Data: dl.DAWID,
},
}, nil
default:
return nil, fmt.Errorf("unknown flow ID %q", dl.FlowID)
}
}

func randomCode() string {
randomStr := crockfordBase32.EncodeToString(random.Bytes(5))
return fmt.Sprintf("%s-%s", randomStr[:4], randomStr[4:])
}

func (dl *DummyLogin) SubmitCookies(ctx context.Context, input map[string]string) (*bridgev2.LoginStep, error) {
return dl.SubmitUserInput(ctx, input)
}

func (dl *DummyLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://random.mau.fi/dummy/api/daw_wait/"+dl.DAWID, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
var input map[string]string
err = json.NewDecoder(resp.Body).Decode(&input)
if err != nil {
return nil, err
}
return dl.SubmitUserInput(ctx, input)
}

func (dl *DummyLogin) SubmitUserInput(ctx context.Context, input map[string]string) (*bridgev2.LoginStep, error) {

0 comments on commit 3ae731a

Please sign in to comment.