summaryrefslogtreecommitdiff
path: root/internal/server/api/api.go
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2025-07-06 14:40:32 +0300
committerKyren223 <Kyren223@proton.me>2025-07-06 14:40:32 +0300
commitf80cb4a0fe26f5ebbb9658da671771dc79d416d6 (patch)
tree78dcfa294aabc1db78686a095c12bf294eaa5145 /internal/server/api/api.go
parente7344492595b26fd7ae21b945d1bdfe2ef115f10 (diff)
Implemented server-side authentication in accordance to the protocol
Diffstat (limited to 'internal/server/api/api.go')
-rw-r--r--internal/server/api/api.go116
1 files changed, 109 insertions, 7 deletions
diff --git a/internal/server/api/api.go b/internal/server/api/api.go
index 068e074..2640433 100644
--- a/internal/server/api/api.go
+++ b/internal/server/api/api.go
@@ -14,6 +14,7 @@ import (
"github.com/kyren223/eko/internal/data"
"github.com/kyren223/eko/internal/packet"
+ "github.com/kyren223/eko/internal/server/ctxkeys"
"github.com/kyren223/eko/internal/server/session"
"github.com/kyren223/eko/pkg/assert"
"github.com/kyren223/eko/pkg/snowflake"
@@ -393,12 +394,13 @@ func CreateNetwork(ctx context.Context, sess *session.Session, request *packet.C
}
}
-func GetNetworksInfo(ctx context.Context, sess *session.Session) (packet.Payload, error) {
+func GetNetworksInfo(ctx context.Context, sess *session.Session) packet.Payload {
var fullNetworks []packet.FullNetwork
tx, err := db.BeginTx(ctx, nil)
if err != nil {
- return nil, err
+ slog.ErrorContext(ctx, "database error", "error", err)
+ return &ErrInternalError
}
defer func() { _ = tx.Rollback() }()
@@ -407,18 +409,21 @@ func GetNetworksInfo(ctx context.Context, sess *session.Session) (packet.Payload
networks, err := qtx.GetUserNetworks(ctx, sess.ID())
if err != nil {
- return nil, err
+ slog.ErrorContext(ctx, "database error", "error", err)
+ return &ErrInternalError
}
for _, network := range networks {
frequencies, err := qtx.GetNetworkFrequencies(ctx, network.ID)
if err != nil {
- return nil, err
+ slog.ErrorContext(ctx, "database error", "error", err)
+ return &ErrInternalError
}
membersAndUsers, err := qtx.GetNetworkMembers(ctx, network.ID)
if err != nil {
- return nil, err
+ slog.ErrorContext(ctx, "database error", "error", err)
+ return &ErrInternalError
}
members, users := SplitMembersAndUsers(membersAndUsers)
@@ -432,14 +437,15 @@ func GetNetworksInfo(ctx context.Context, sess *session.Session) (packet.Payload
err = tx.Commit()
if err != nil {
- return nil, err
+ slog.ErrorContext(ctx, "database error", "error", err)
+ return &ErrInternalError
}
return &packet.NetworksInfo{
Networks: fullNetworks,
RemovedNetworks: nil,
Partial: false,
- }, nil
+ }
}
func CreateFrequency(ctx context.Context, sess *session.Session, request *packet.CreateFrequency) packet.Payload {
@@ -1565,3 +1571,99 @@ func GetUsers(ctx context.Context, sess *session.Session, request *packet.GetUse
Users: users,
}
}
+
+func GetNonce(ctx context.Context, sess *session.Session, request *packet.GetNonce) packet.Payload {
+ return &packet.NonceInfo{
+ Nonce: sess.Challenge(),
+ }
+}
+
+func Authenticate(ctx context.Context, sess *session.Session, request *packet.Authenticate) packet.Payload {
+ if sess.IsAuthenticated() {
+ return &packet.Error{Error: "already authenticated"}
+ }
+
+ if len(request.PubKey) != ed25519.PublicKeySize {
+ return &packet.Error{Error: fmt.Sprintf(
+ "public key must be exactly %v bytes", ed25519.PublicKeySize,
+ )}
+ }
+
+ if len(request.Signature) != ed25519.SignatureSize {
+ return &packet.Error{Error: fmt.Sprintf(
+ "signature must be exactly %v bytes", ed25519.SignatureSize,
+ )}
+ }
+
+ // IMPORTANT
+ if ok := ed25519.Verify(request.PubKey, sess.Challenge(), request.Signature); !ok {
+ return &packet.Error{Error: "signature verification failed"}
+ }
+
+ // Authenticated from here on out
+
+ queries := data.New(db)
+
+ user, err := queries.GetUserByPublicKey(ctx, request.PubKey)
+ if err == sql.ErrNoRows {
+ id := sess.Manager().Node().Generate()
+ user, err = queries.CreateUser(ctx, data.CreateUserParams{
+ ID: id,
+ Name: "User" + strconv.FormatInt(id.Time()%1000, 10),
+ PublicKey: request.PubKey,
+ })
+ }
+ if err != nil {
+ slog.ErrorContext(ctx, "database error", "error", err)
+ return &ErrInternalError
+ }
+
+ if user.IsDeleted {
+ return &packet.Error{Error: "public key is already taken by a deleted user"}
+ }
+
+ sess.Manager().AddSession(sess, user.ID, request.PubKey)
+
+ // NOTE: as per the protocol, this must be the first message after auth
+ payload := &packet.UsersInfo{Users: []data.User{user}}
+ ok := sess.Write(ctx, WrapPayload(payload))
+ if !ok {
+ // Timeout, send at least this payload, client can request the rest
+ return payload
+ }
+
+ ok = sendInitialAuthPackets(ctx, sess) // Send rest of packets
+ if !ok {
+ // Timeout, notify the client at least
+ return &packet.Error{Error: "timeout: not all initial auth packets were sent"}
+ }
+
+ return nil // manually writing requests to control order
+}
+
+func sendInitialAuthPackets(ctx context.Context, sess *session.Session) bool {
+ payloads := []packet.Payload{}
+
+ payloads = append(payloads, GetUserData(ctx, sess, &packet.GetUserData{}))
+ payloads = append(payloads, GetTrustedUsers(ctx, sess))
+ payloads = append(payloads, GetBlockedUsers(ctx, sess))
+ payloads = append(payloads, GetBlockedUsers(ctx, sess))
+ payloads = append(payloads, GetNetworksInfo(ctx, sess))
+ payloads = append(payloads, GetNotifications(ctx, sess))
+
+ success := true
+ for _, payload := range payloads {
+ if payload == &ErrInternalError {
+ success = false
+ continue
+ }
+ slog.InfoContext(ctx, "sending initial auth payload", ctxkeys.Payload.String(), payload, ctxkeys.PayloadType.String(), payload.Type())
+ ok := sess.Write(ctx, WrapPayload(payload))
+ if !ok {
+ success = false
+ continue
+ }
+ }
+
+ return success
+}