diff options
| author | Kyren223 <Kyren223@proton.me> | 2025-07-06 14:40:32 +0300 |
|---|---|---|
| committer | Kyren223 <Kyren223@proton.me> | 2025-07-06 14:40:32 +0300 |
| commit | f80cb4a0fe26f5ebbb9658da671771dc79d416d6 (patch) | |
| tree | 78dcfa294aabc1db78686a095c12bf294eaa5145 /internal/server/api | |
| parent | e7344492595b26fd7ae21b945d1bdfe2ef115f10 (diff) | |
Implemented server-side authentication in accordance to the protocol
Diffstat (limited to 'internal/server/api')
| -rw-r--r-- | internal/server/api/api.go | 116 | ||||
| -rw-r--r-- | internal/server/api/helpers.go | 10 |
2 files changed, 115 insertions, 11 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 +} diff --git a/internal/server/api/helpers.go b/internal/server/api/helpers.go index 4327a49..2ed0694 100644 --- a/internal/server/api/helpers.go +++ b/internal/server/api/helpers.go @@ -79,8 +79,7 @@ func NetworkPropagateWithFilter( context, cancel := context.WithTimeout(context.Background(), timeout) go func() { defer cancel() - pkt := packet.NewPacket(packet.NewMsgPackEncoder(payload)) - if ok := session.Write(context, pkt); !ok { + if ok := session.Write(context, WrapPayload(payload)); !ok { log.Println(sess.Addr(), "propagation to", session.Addr(), "failed") } }() @@ -122,8 +121,7 @@ func UserPropagate( context, cancel := context.WithTimeout(context.Background(), timeout) go func() { defer cancel() - pkt := packet.NewPacket(packet.NewMsgPackEncoder(payload)) - if ok := session.Write(context, pkt); !ok { + if ok := session.Write(context, WrapPayload(payload)); !ok { log.Println(sess.Addr(), "propagation to", session.Addr(), "failed") } }() @@ -191,3 +189,7 @@ func getNotifications(ctx context.Context, userId snowflake.ID) (packet.Notifica } return items, nil } + +func WrapPayload(payload packet.Payload) packet.Packet { + return packet.NewPacket(packet.NewMsgPackEncoder(payload)) +} |
