Auth API
This commit is contained in:
parent
ecdb944daa
commit
0a5c8ad3e9
187
auth/auth.go
Normal file
187
auth/auth.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lookupEmail(conn *pgx.Conn, username string) (email string, err error) {
|
||||||
|
query := "select id from accounts where username=$1"
|
||||||
|
rows, err := conn.Query(context.Background(), query, username)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ids []int
|
||||||
|
for rows.Next() {
|
||||||
|
var id int
|
||||||
|
rows.Scan(&id)
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
query = "select email from users where account_id=$1"
|
||||||
|
err = conn.QueryRow(context.Background(), query, id).Scan(&email)
|
||||||
|
if err == nil && email != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupUsername(conn *pgx.Conn, email string) (username string, err error) {
|
||||||
|
query := "select account_id from users where email=$1"
|
||||||
|
var id int
|
||||||
|
err = conn.QueryRow(context.Background(), query, email).Scan(&id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
query = "select username from accounts where id=$1"
|
||||||
|
err = conn.QueryRow(context.Background(), query, id).Scan(&username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func auth(conn *pgx.Conn, email, password string) (err error) {
|
||||||
|
query := "select account_id,confirmed_at,approved,disabled," +
|
||||||
|
"encrypted_password from users where email=$1"
|
||||||
|
|
||||||
|
var id int
|
||||||
|
var confirmed pgtype.Timestamptz
|
||||||
|
var approved, disabled bool
|
||||||
|
var hash string
|
||||||
|
|
||||||
|
err = conn.QueryRow(context.Background(), query, email).
|
||||||
|
Scan(&id, &confirmed, &approved, &disabled, &hash)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !confirmed.Valid {
|
||||||
|
return errors.New("account is not confirmed")
|
||||||
|
}
|
||||||
|
if !approved {
|
||||||
|
return errors.New("account is not approved")
|
||||||
|
}
|
||||||
|
if disabled {
|
||||||
|
return errors.New("account disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
query = "select suspended_at from accounts where id=$1"
|
||||||
|
|
||||||
|
var suspended pgtype.Timestamptz
|
||||||
|
|
||||||
|
err = conn.QueryRow(context.Background(), query, id).Scan(&suspended)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if suspended.Valid {
|
||||||
|
return errors.New("account suspended")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
Banlist map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, banned := h.Banlist[r.Header.Get("X-Forwarded-For")]
|
||||||
|
if banned {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method != "POST" {
|
||||||
|
fmt.Println("method is not POST", r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var params struct {
|
||||||
|
Secret, Username, Email, Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Secret != os.Getenv("AUTH_SECRET") {
|
||||||
|
fmt.Println("wrong secret", r)
|
||||||
|
h.Banlist[r.Header.Get("X-Forwarded-For")] = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Username, Email, Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
buf, _ := json.Marshal(response)
|
||||||
|
w.Write(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
|
path := "dbname=" + os.Getenv("DATABASE")
|
||||||
|
conn, err := pgx.Connect(context.Background(), path)
|
||||||
|
if err != nil {
|
||||||
|
response.Error = fmt.Sprint(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close(context.Background())
|
||||||
|
|
||||||
|
if params.Email == "" {
|
||||||
|
params.Email, err = lookupEmail(conn, params.Username)
|
||||||
|
if err != nil {
|
||||||
|
response.Error = fmt.Sprint(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Username == "" {
|
||||||
|
params.Username, err = lookupUsername(conn, params.Email)
|
||||||
|
if err != nil {
|
||||||
|
response.Error = fmt.Sprint(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = auth(conn, params.Email, params.Password)
|
||||||
|
if err != nil {
|
||||||
|
response.Error = fmt.Sprint(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Username = params.Username
|
||||||
|
response.Email = params.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if os.Getenv("AUTH_SECRET") == "" {
|
||||||
|
panic("AUTH_SECRET is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
socket, err := net.Listen("unix", os.Getenv("SOCKET"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Chmod(os.Getenv("SOCKET"), 0770)
|
||||||
|
|
||||||
|
server := http.Server{
|
||||||
|
Handler: &handler{Banlist: make(map[string]bool)},
|
||||||
|
}
|
||||||
|
server.Serve(socket)
|
||||||
|
}
|
14
auth/go.mod
Normal file
14
auth/go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module code.dumpstack.io/infra/lor.sh/auth
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgx/v5 v5.2.0
|
||||||
|
golang.org/x/crypto v0.5.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
|
golang.org/x/text v0.6.0 // indirect
|
||||||
|
)
|
21
auth/go.sum
Normal file
21
auth/go.sum
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
|
||||||
|
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||||
|
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||||
|
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||||
|
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||||
|
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
28
mastodon.nix
28
mastodon.nix
@ -64,10 +64,34 @@ let
|
|||||||
$@
|
$@
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
auth = pkgs.buildGoModule rec {
|
||||||
|
name = "auth";
|
||||||
|
src = ./auth;
|
||||||
|
vendorHash = "sha256-cLn1tZL+LVMmSpLZYA7uRkEW7eFWGf+NFdvBEvQtjH4=";
|
||||||
|
};
|
||||||
|
|
||||||
bucket = secrets.backup.bucket;
|
bucket = secrets.backup.bucket;
|
||||||
|
|
||||||
domainName = "lor.sh";
|
domainName = "lor.sh";
|
||||||
in {
|
in {
|
||||||
|
systemd.services."mastodon-auth" = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" ];
|
||||||
|
environment = {
|
||||||
|
SOCKET = "/var/run/mastodon-auth/auth.socket";
|
||||||
|
DATABASE = "mastodon";
|
||||||
|
AUTH_SECRET = secrets.authSecret;
|
||||||
|
};
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 30;
|
||||||
|
ExecStart = "${auth}/bin/auth";
|
||||||
|
User = "mastodon";
|
||||||
|
RuntimeDirectory = "mastodon-auth";
|
||||||
|
RuntimeDirectoryMode = "0750";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
services.postgresqlBackup = {
|
services.postgresqlBackup = {
|
||||||
enable = true;
|
enable = true;
|
||||||
databases = [ "mastodon" ];
|
databases = [ "mastodon" ];
|
||||||
@ -110,6 +134,10 @@ in {
|
|||||||
reverse_proxy unix//run/mastodon-streaming/streaming.socket
|
reverse_proxy unix//run/mastodon-streaming/streaming.socket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle /api/v0/auth* {
|
||||||
|
reverse_proxy unix//run/mastodon-auth/auth.socket
|
||||||
|
}
|
||||||
|
|
||||||
handle {
|
handle {
|
||||||
reverse_proxy unix//run/mastodon-web/web.socket
|
reverse_proxy unix//run/mastodon-web/web.socket
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
bucket = "";
|
bucket = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
authSecret = "";
|
||||||
|
|
||||||
smtpPassword = "";
|
smtpPassword = "";
|
||||||
|
|
||||||
vapidPublicKey = "";
|
vapidPublicKey = "";
|
||||||
|
Loading…
Reference in New Issue
Block a user