add main project files
This commit is contained in:
parent
5f82ac25cb
commit
8fc677a17f
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
||||
/dist
|
||||
/bin
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.mktools/
|
||||
/dist
|
||||
/bin
|
10
.goreleaser.yaml
Normal file
10
.goreleaser.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
project_name: altcha
|
||||
builds:
|
||||
- id: altcha
|
||||
main: ./cmd/altcha
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- "386"
|
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
FROM reg.cadoles.com/proxy_cache/library/golang:1.23.1 AS build
|
||||
|
||||
RUN apt-get update && apt-get install make
|
||||
|
||||
COPY . /src
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
RUN go mod download && make GORELEASER_ARGS="build --rm-dist --single-target --snapshot" goreleaser
|
||||
|
||||
FROM reg.cadoles.com/proxy_cache/library/busybox
|
||||
|
||||
COPY --from=build /src/dist/altcha_linux_amd64_v1 /app
|
||||
RUN chown -R 1000:1000 /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
CMD ["bin/altcha", "run"]
|
58
Makefile
Normal file
58
Makefile
Normal file
@ -0,0 +1,58 @@
|
||||
IMAGE_REPO ?= reg.cadoles.com/cadoles/altcha
|
||||
|
||||
GORELEASER_VERSION ?= v1.13.1
|
||||
GORELEASER_ARGS ?= release --snapshot --rm-dist
|
||||
|
||||
ALTCHA_VERSION ?=
|
||||
GIT_COMMIT := $(shell git rev-parse --short HEAD)
|
||||
DATE_VERSION := $(shell date +%Y.%-m.%-d)
|
||||
FULL_VERSION := v$(DATE_VERSION)-$(GIT_COMMIT)$(if $(shell git diff --stat),-dirty,)
|
||||
|
||||
.PHONY: test
|
||||
test: test-go ## Executing tests
|
||||
|
||||
test-go:
|
||||
go test -v -race ./...
|
||||
|
||||
build: build-altcha
|
||||
|
||||
build-altcha: ## Build executable
|
||||
CGO_ENABLED=0 go build \
|
||||
-v \
|
||||
-ldflags "\
|
||||
-X 'main.GitRef=$(shell git rev-parse --short HEAD)' \
|
||||
-X 'main.ProjectVersion=$(FULL_VERSION)' \
|
||||
-X 'main.BuildDate=$(shell date --utc --rfc-3339=seconds)' \
|
||||
" \
|
||||
-o ./bin/altcha \
|
||||
./cmd/altcha
|
||||
|
||||
run:
|
||||
bin/altcha $(ATLCHA_CMD)
|
||||
|
||||
.PHONY: goreleaser
|
||||
goreleaser:
|
||||
curl -sfL https://goreleaser.com/static/run | VERSION=$(GORELEASER_VERSION) GORELEASER_CURRENT_TAG="$(FULL_VERSION)" bash /dev/stdin $(GORELEASER_ARGS)
|
||||
|
||||
build-image:
|
||||
docker build \
|
||||
-t "$(IMAGE_REPO):latest" \
|
||||
.
|
||||
|
||||
release-image: .mktools
|
||||
@[ ! -z "$(MKT_PROJECT_VERSION)" ] || ( echo "Just downloaded mktools. Please re-run command."; exit 1 )
|
||||
docker tag "$(IMAGE_REPO):latest" "$(IMAGE_REPO):$(MKT_PROJECT_VERSION)"
|
||||
docker tag "$(IMAGE_REPO):latest" "$(IMAGE_REPO):$(MKT_PROJECT_SHORT_VERSION)"
|
||||
docker push "$(IMAGE_REPO):latest"
|
||||
docker push "$(IMAGE_REPO):$(MKT_PROJECT_VERSION)"
|
||||
docker push "$(IMAGE_REPO):$(MKT_PROJECT_SHORT_VERSION)"
|
||||
|
||||
.PHONY: mktools
|
||||
mktools:
|
||||
rm -rf .mktools
|
||||
curl -q https://forge.cadoles.com/Cadoles/mktools/raw/branch/master/install.sh | $(SHELL)
|
||||
|
||||
.mktools:
|
||||
$(MAKE) mktools
|
||||
|
||||
-include .mktools/*.mk
|
21
README.md
21
README.md
@ -0,0 +1,21 @@
|
||||
# Altcha server
|
||||
|
||||
Serveur de génération de challenges altcha et de validation de la solution
|
||||
|
||||
# Utilisation
|
||||
|
||||
Lancer le serveur
|
||||
```
|
||||
altcha run
|
||||
```
|
||||
|
||||
# Variables d'environement
|
||||
| Nom | Description | Valeur par défaut | Requis |
|
||||
|---------------------|------------------------------------------------------------------------------|--------------------------|--------|
|
||||
| ALTCHA_PORT | Port d'écoute du serveur | 3333 | Non |
|
||||
| ALTCHA_HMAC_KEY | Clé d'encodage des signatures | | Oui |
|
||||
| ALTCHA_MAX_NUMBER | Nombre d'itération maximum pour résoudre le challenge (défini la difficulté) | 1000000 | Non |
|
||||
| ALTCHA_ALGORITHM | Algorithme de hashage (valeurs possibles: SHA-1, SHA-256, SHA-512) | SHA-256 | Non |
|
||||
| ALTCHA_SALT | Forcer le salt du challenge | *Généré automatiquement* | Non |
|
||||
| ALTCHA_EXPIRE | Temps avant expiration du challenge (en secondes) | 600 | Non |
|
||||
| ALTCHA_CHECK_EXPIRE | Vérifier si le challenge à expiré | 1 | Non |
|
14
cmd/altcha/main.go
Normal file
14
cmd/altcha/main.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/command"
|
||||
)
|
||||
|
||||
func main() {
|
||||
command.Main(
|
||||
command.RunCommand(),
|
||||
command.GenerateCommand(),
|
||||
command.SolveCommand(),
|
||||
command.VerifyCommand(),
|
||||
)
|
||||
}
|
35
go.mod
Normal file
35
go.mod
Normal file
@ -0,0 +1,35 @@
|
||||
module forge.cadoles.com/cadoles/altcha-server
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/altcha-org/altcha-lib-go v0.1.3
|
||||
github.com/caarlos0/env/v11 v11.2.2
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/urfave/cli/v2 v2.27.4
|
||||
gitlab.com/wpetit/goweb v0.0.0-20240226160244-6b2826c79f88
|
||||
)
|
||||
|
||||
require (
|
||||
cdr.dev/slog v1.6.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.13.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
)
|
96
go.sum
Normal file
96
go.sum
Normal file
@ -0,0 +1,96 @@
|
||||
cdr.dev/slog v1.6.1 h1:IQjWZD0x6//sfv5n+qEhbu3wBkmtBQY5DILXNvMaIv4=
|
||||
cdr.dev/slog v1.6.1/go.mod h1:eHEYQLaZvxnIAXC+XdTSNLb/kgA/X2RVSF72v5wsxEI=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I=
|
||||
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
|
||||
cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
|
||||
cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/altcha-org/altcha-lib-go v0.1.3 h1:eW0T6gs4tqKjCIm5QZwerj++IMx2UHq8lFlrtzfIwGg=
|
||||
github.com/altcha-org/altcha-lib-go v0.1.3/go.mod h1:I8ESLVWR9C58uvGufB/AJDPhaSU4+4Oh3DLpVtgwDAk=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/caarlos0/env/v11 v11.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg=
|
||||
github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc=
|
||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
||||
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
|
||||
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
|
||||
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
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/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
|
||||
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
gitlab.com/wpetit/goweb v0.0.0-20240226160244-6b2826c79f88 h1:dsyRrmhp7fl/YaY1YIzz7lm9qfIFI5KpKNbXwuhTULA=
|
||||
gitlab.com/wpetit/goweb v0.0.0-20240226160244-6b2826c79f88/go.mod h1:bg+TN16Rq2ygLQbB4VDSHQFNouAEzcy3AAutStehllA=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
|
||||
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg=
|
||||
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o=
|
||||
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
||||
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
177
internal/api/server.go
Normal file
177
internal/api/server.go
Normal file
@ -0,0 +1,177 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/client"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/config"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/render"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
port string
|
||||
client client.Client
|
||||
}
|
||||
|
||||
func (s *Server) Run(ctx context.Context) {
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(corsMiddleware)
|
||||
r.Use(render.SetContentType(render.ContentTypeJSON))
|
||||
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("root."))
|
||||
})
|
||||
r.Get("/request", s.requestHandler)
|
||||
r.Get("/verify", s.submitHandler)
|
||||
r.Get("/verify-spam-filter", s.submitSpamFilterHandler)
|
||||
|
||||
logger.Info(ctx, "altcha server listening on port "+s.port)
|
||||
if err := http.ListenAndServe(":"+s.port, r); err != nil {
|
||||
logger.Error(ctx, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) requestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
challenge, err := s.client.Generate()
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to create challenge : %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, challenge)
|
||||
}
|
||||
|
||||
func (s *Server) submitHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
formData := r.FormValue("altcha")
|
||||
if formData == "" {
|
||||
http.Error(w, "Atlcha payload missing", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
decodedPayload, err := base64.StdEncoding.DecodeString(formData)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to decode Altcha payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var payload map[string]interface{}
|
||||
if err := json.Unmarshal(decodedPayload, &payload); err != nil {
|
||||
http.Error(w, "Failed to parse Altcha payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
verified, err := s.client.VerifySolution(payload)
|
||||
|
||||
if err != nil || !verified {
|
||||
http.Error(w, "Invalid Altcha payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": true,
|
||||
"data": formData,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) submitSpamFilterHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
formData, err := formToMap(r)
|
||||
if err != nil {
|
||||
http.Error(w, "Cannot read form data", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
payload := r.FormValue("altcha")
|
||||
if payload == "" {
|
||||
http.Error(w, "Atlcha payload missing", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
verified, verificationData, err := s.client.VerifyServerSignature(payload)
|
||||
if err != nil || !verified {
|
||||
http.Error(w, "Invalid Altcha payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if verificationData.Verified && verificationData.Expire > time.Now().Unix() {
|
||||
if verificationData.Classification == "BAD" {
|
||||
http.Error(w, "Classified as spam", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if verificationData.FieldsHash != "" {
|
||||
verified, err := s.client.VerifyFieldsHash(formData, verificationData.Fields, verificationData.FieldsHash)
|
||||
if err != nil || !verified {
|
||||
http.Error(w, "Invalid fields hash", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": true,
|
||||
"data": formData,
|
||||
"verificationData": verificationData,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Invalid Altcha payload", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func corsMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(data); err != nil {
|
||||
http.Error(w, "Failed to encode JSON", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func formToMap(r *http.Request) (map[string][]string, error) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Form, nil
|
||||
}
|
||||
|
||||
func NewServer(cfg config.Config) *Server {
|
||||
client := *client.NewClient(cfg.HmacKey, cfg.MaxNumber, cfg.Algorithm, cfg.Salt, cfg.Expire, cfg.CheckExpire)
|
||||
|
||||
return &Server {
|
||||
port: cfg.Port,
|
||||
client: client,
|
||||
}
|
||||
}
|
61
internal/client/main.go
Normal file
61
internal/client/main.go
Normal file
@ -0,0 +1,61 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/altcha-org/altcha-lib-go"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
hmacKey string
|
||||
maxNumber int64
|
||||
algorithm altcha.Algorithm
|
||||
salt string
|
||||
expire string
|
||||
checkExpire bool
|
||||
}
|
||||
|
||||
func NewClient(hmacKey string, maxNumber int64, algorithm string, salt string, expire string, checkExpire bool) *Client {
|
||||
return &Client {
|
||||
hmacKey: hmacKey,
|
||||
maxNumber: maxNumber,
|
||||
algorithm: altcha.Algorithm(algorithm),
|
||||
salt: salt,
|
||||
expire: expire,
|
||||
checkExpire: checkExpire,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Generate() (altcha.Challenge, error) {
|
||||
expirationDuration, _ := time.ParseDuration(c.expire+"s")
|
||||
expiration := time.Now().Add(expirationDuration)
|
||||
|
||||
options := altcha.ChallengeOptions{
|
||||
HMACKey: c.hmacKey,
|
||||
MaxNumber: c.maxNumber,
|
||||
Algorithm: c.algorithm,
|
||||
Expires: &expiration,
|
||||
}
|
||||
|
||||
if len(c.salt) > 0 {
|
||||
options.Salt = c.salt
|
||||
}
|
||||
|
||||
return altcha.CreateChallenge(options)
|
||||
}
|
||||
|
||||
func (c *Client) Solve(challenge string) (*altcha.Solution, error) {
|
||||
return altcha.SolveChallenge(challenge, c.salt, c.algorithm, int(c.maxNumber), 0, make(<-chan struct{}))
|
||||
}
|
||||
|
||||
func (c *Client) VerifySolution(payload interface{}) (bool, error) {
|
||||
return altcha.VerifySolution(payload, c.hmacKey, c.checkExpire)
|
||||
}
|
||||
|
||||
func (c *Client) VerifyServerSignature(payload interface{}) (bool, altcha.ServerSignatureVerificationData, error) {
|
||||
return altcha.VerifyServerSignature(payload, c.hmacKey)
|
||||
}
|
||||
|
||||
func (c *Client) VerifyFieldsHash(formData map[string][]string, fields []string, fieldsHash string) (bool, error) {
|
||||
return altcha.VerifyFieldsHash(formData, fields, fieldsHash, c.algorithm)
|
||||
}
|
7
internal/command/common/flags.go
Normal file
7
internal/command/common/flags.go
Normal file
@ -0,0 +1,7 @@
|
||||
package common
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
func Flags() []cli.Flag {
|
||||
return []cli.Flag{}
|
||||
}
|
40
internal/command/generate.go
Normal file
40
internal/command/generate.go
Normal file
@ -0,0 +1,40 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/client"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/command/common"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/config"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
func GenerateCommand() *cli.Command {
|
||||
flags := common.Flags()
|
||||
|
||||
return &cli.Command{
|
||||
Name: "generate",
|
||||
Usage: "generate a challenge",
|
||||
Flags: flags,
|
||||
Action: func(ctx *cli.Context) error {
|
||||
cfg := config.Config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
c := client.NewClient(cfg.HmacKey, cfg.MaxNumber, cfg.Algorithm, cfg.Salt, cfg.Expire, cfg.CheckExpire)
|
||||
|
||||
challenge, err := c.Generate()
|
||||
if err != nil {
|
||||
logger.Error(ctx.Context, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", challenge)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
49
internal/command/main.go
Normal file
49
internal/command/main.go
Normal file
@ -0,0 +1,49 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func Main(commands ...*cli.Command) {
|
||||
ctx := context.Background()
|
||||
|
||||
app := &cli.App {
|
||||
Version: "1",
|
||||
Name: "altcha-server",
|
||||
Usage: "create challenges and validate solutions for atlcha captcha",
|
||||
Commands: commands,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
EnvVars: []string{"ALTCHA_DEBUG"},
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.ExitErrHandler = func (ctx *cli.Context, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
debug := ctx.Bool("debug")
|
||||
|
||||
if !debug {
|
||||
fmt.Printf("[ERROR] %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(cli.FlagsByName(app.Flags))
|
||||
sort.Sort(cli.CommandsByName(app.Commands))
|
||||
|
||||
if err := app.RunContext(ctx, os.Args); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
30
internal/command/run.go
Normal file
30
internal/command/run.go
Normal file
@ -0,0 +1,30 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/api"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/command/common"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/config"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func RunCommand() *cli.Command {
|
||||
flags := common.Flags()
|
||||
|
||||
return &cli.Command{
|
||||
Name: "run",
|
||||
Usage: "run the atlcha api server",
|
||||
Flags: flags,
|
||||
Action: func(ctx *cli.Context) error {
|
||||
cfg := config.Config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
api.NewServer(cfg).Run(ctx.Context)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
47
internal/command/solve.go
Normal file
47
internal/command/solve.go
Normal file
@ -0,0 +1,47 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/client"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/command/common"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/config"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
func SolveCommand() *cli.Command {
|
||||
flags := common.Flags()
|
||||
|
||||
return &cli.Command{
|
||||
Name: "solve",
|
||||
Usage: "solve the challenge and return the solution",
|
||||
Flags: flags,
|
||||
Args: true,
|
||||
ArgsUsage: "[CHALLENGE] [SALT]",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
cfg := config.Config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
challenge := ctx.Args().Get(0)
|
||||
salt := ctx.Args().Get(1)
|
||||
|
||||
c := client.NewClient(cfg.HmacKey, cfg.MaxNumber, cfg.Algorithm, salt, cfg.Expire, cfg.CheckExpire)
|
||||
|
||||
|
||||
solution, err := c.Solve(challenge)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(ctx.Context, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", solution)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
56
internal/command/verify.go
Normal file
56
internal/command/verify.go
Normal file
@ -0,0 +1,56 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/client"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/command/common"
|
||||
"forge.cadoles.com/cadoles/altcha-server/internal/config"
|
||||
"github.com/altcha-org/altcha-lib-go"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func VerifyCommand() *cli.Command {
|
||||
flags := common.Flags()
|
||||
|
||||
return &cli.Command{
|
||||
Name: "verify",
|
||||
Usage: "verify the solution",
|
||||
Flags: flags,
|
||||
Args: true,
|
||||
ArgsUsage: "[challenge] [salt] [signature] [solution]",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
cfg := config.Config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
challenge := ctx.Args().Get(0)
|
||||
salt := ctx.Args().Get(1)
|
||||
signature := ctx.Args().Get(2)
|
||||
solution, _ := strconv.ParseInt(ctx.Args().Get(3), 10, 64)
|
||||
|
||||
c := client.NewClient(cfg.HmacKey, cfg.MaxNumber, cfg.Algorithm, cfg.Salt, cfg.Expire, cfg.CheckExpire)
|
||||
|
||||
payload := altcha.Payload{
|
||||
Algorithm: cfg.Algorithm,
|
||||
Challenge: challenge,
|
||||
Number: solution,
|
||||
Salt: salt,
|
||||
Signature: signature,
|
||||
}
|
||||
|
||||
verified, err := c.VerifySolution(payload)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(verified)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
11
internal/config/config.go
Normal file
11
internal/config/config.go
Normal file
@ -0,0 +1,11 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
Port string `env:"ALTCHA_PORT" envDefault:"3333"`
|
||||
HmacKey string `env:"ALTCHA_HMAC_KEY"`
|
||||
MaxNumber int64 `env:"ALTCHA_MAX_NUMBER" envDefault:"1000000"`
|
||||
Algorithm string `env:"ALTCHA_ALGORITHM" envDefault:"SHA-256"`
|
||||
Salt string `env:"ALTCHA_SALT"`
|
||||
Expire string `env:"ALTCHA_EXPIRE" envDefault:"600"`
|
||||
CheckExpire bool `env:"ALTCHA_CHECK_EXPIRE" envDefault:"1"`
|
||||
}
|
Loading…
Reference in New Issue
Block a user