Skip to content

feat(auth): Implement Session Authentication & Deprecate REST #1196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agent/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ var getConfig = &cobra.Command{
cmd.Println()
cmd.Println("Haproxy Configuration:")
cmd.Printf(" • Enabled ------------ %t\n", config.HaproxyConfig.Enabled)
cmd.Printf(" • Username ---------- %s\n", config.HaproxyConfig.Username)
cmd.Printf(" • userID ---------- %s\n", config.HaproxyConfig.Username)
cmd.Printf(" • Password ---------- %s\n", config.HaproxyConfig.Password)
},
}
Expand Down
4 changes: 2 additions & 2 deletions agent/haproxy_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func installHAProxy(swiftwaveAddress string, dnsServer string, username string,

// Write default dataplaneapi config
dataplaneapiConfigContent := dataplaneapiConfig
dataplaneapiConfigContent = strings.ReplaceAll(dataplaneapiConfigContent, "{{ .Username }}", username)
dataplaneapiConfigContent = strings.ReplaceAll(dataplaneapiConfigContent, "{{ .userID }}", username)
passwordHash, err := GenerateBasicAuthPassword(password)
if err != nil {
return err
Expand Down Expand Up @@ -175,7 +175,7 @@ dataplaneapi:
scheme:
- http
user:
- name: {{ .Username }}
- name: {{ .userID }}
insecure: false
password: {{ .Password }}
resources:
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang-migrate/migrate/v4 v4.17.1
github.com/hashicorp/go-set v0.1.14
github.com/labstack/echo-jwt/v4 v4.3.0
github.com/labstack/echo/v4 v4.13.3
github.com/lib/pq v1.10.9
github.com/mholt/acmez v1.2.0
Expand Down Expand Up @@ -46,7 +45,7 @@ require (
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/gorilla/websocket v1.5.2
github.com/gorilla/websocket v1.5.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
Expand Down Expand Up @@ -138,7 +137,7 @@ require (
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/errors v0.9.1
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.uber.org/atomic v1.11.0 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo-jwt/v4 v4.3.0 h1:8JcvVCrK9dRkPx/aWY3ZempZLO336Bebh4oAtBcxAv4=
github.com/labstack/echo-jwt/v4 v4.3.0/go.mod h1:OlWm3wqfnq3Ma8DLmmH7GiEAz2S7Bj23im2iPMEAR+Q=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
Expand Down
12 changes: 6 additions & 6 deletions swiftwave_service/cmd/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ func init() {
userManagementCmd.AddCommand(createUserCmd)
userManagementCmd.AddCommand(deleteUserCmd)
userManagementCmd.AddCommand(disableTotpCmd)
createUserCmd.Flags().StringP("username", "u", "", "Username")
createUserCmd.Flags().StringP("username", "u", "", "userID")
createUserCmd.Flags().StringP("password", "p", "", "Password [Optional]")
deleteUserCmd.Flags().StringP("username", "u", "", "Username")
disableTotpCmd.Flags().StringP("username", "u", "", "Username")
deleteUserCmd.Flags().StringP("username", "u", "", "userID")
disableTotpCmd.Flags().StringP("username", "u", "", "userID")
}

var userManagementCmd = &cobra.Command{
Expand All @@ -41,7 +41,7 @@ var createUserCmd = &cobra.Command{
username := cmd.Flag("username").Value.String()
password := cmd.Flag("password").Value.String()
if username == "" {
printError("Username is required")
printError("userID is required")
err := cmd.Help()
if err != nil {
return
Expand Down Expand Up @@ -108,7 +108,7 @@ var deleteUserCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
username := cmd.Flag("username").Value.String()
if username == "" {
printError("Username is required")
printError("userID is required")
err := cmd.Help()
if err != nil {
return
Expand Down Expand Up @@ -146,7 +146,7 @@ var disableTotpCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
username := cmd.Flag("username").Value.String()
if username == "" {
printError("Username is required")
printError("userID is required")
err := cmd.Help()
if err != nil {
return
Expand Down
21 changes: 15 additions & 6 deletions swiftwave_service/core/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,21 @@ type ServerLog struct {

// User hold information about user
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
Username string `json:"username" gorm:"unique"`
Role UserRole `json:"role" gorm:"default:'user'"`
PasswordHash string `json:"password_hash"`
TotpEnabled bool `json:"totp_enabled" gorm:"default:false"`
TotpSecret string `json:"totp_secret"`
ID uint `json:"id" gorm:"primaryKey"`
Username string `json:"username" gorm:"unique"`
Role UserRole `json:"role" gorm:"default:'user'"`
PasswordHash string `json:"password_hash"`
TotpEnabled bool `json:"totp_enabled" gorm:"default:false"`
TotpSecret string `json:"totp_secret"`
Sessions []UserSession `json:"sessions" gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}

// UserSession hold information about
type UserSession struct {
ID uint `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id"`
SessionID string `json:"session_id" gorm:"index"`
ExpiresAt time.Time `json:"expires_at"`
}

// ************************************************************************************* //
Expand Down
45 changes: 45 additions & 0 deletions swiftwave_service/core/user_session.operations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package core

import (
"context"
"errors"
"github.com/labstack/gommon/random"
"gorm.io/gorm"
"time"
)

// This file contains the operations for the User Sessions model.
// This functions will perform necessary validation before doing the actual database operation.

// Each function's argument format should be (ctx context.Context, db gorm.DB, ...)
// context used to pass some data to the function e.g. user id, auth info, etc.

// CreateSession : create session for user
func CreateSession(ctx context.Context, db gorm.DB, user User) (string, error) {
sessionRecord := &UserSession{
UserID: user.ID,
SessionID: random.String(128),
ExpiresAt: time.Now().Add(time.Hour * 720),
}
// Create record
err := db.Create(sessionRecord).Error
if err != nil {
return "", err
}
return sessionRecord.SessionID, nil
}

// DeleteSessionBySessionID : delete session record by session id
func DeleteSessionBySessionID(ctx context.Context, db gorm.DB, sessionID string) error {
return db.Where("session_id = ?", sessionID).Delete(&UserSession{}).Error
}

// FetchUserIDBySessionID : get user by session id
func FetchUserIDBySessionID(ctx context.Context, db gorm.DB, sessionID string) (uint, error) {
var session UserSession
err := db.Where("session_id = ?", sessionID).Select("user_id").First(&session).Error
if err != nil {
return 0, errors.New("invalid session")
}
return session.UserID, nil
}
4 changes: 4 additions & 0 deletions swiftwave_service/db/migrations/20250213190430_test.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- reverse: create index "idx_user_sessions_session_id" to table: "user_sessions"
DROP INDEX "public"."idx_user_sessions_session_id";
-- reverse: create "user_sessions" table
DROP TABLE "public"."user_sessions";
11 changes: 11 additions & 0 deletions swiftwave_service/db/migrations/20250213190430_test.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- create "user_sessions" table
CREATE TABLE "public"."user_sessions" (
"id" bigserial NOT NULL,
"user_id" bigint NULL,
"session_id" text NULL,
"expires_at" timestamptz NULL,
PRIMARY KEY ("id"),
CONSTRAINT "fk_users_sessions" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("id") ON UPDATE CASCADE ON DELETE CASCADE
);
-- create index "idx_user_sessions_session_id" to table: "user_sessions"
CREATE INDEX "idx_user_sessions_session_id" ON "public"."user_sessions" ("session_id");
4 changes: 3 additions & 1 deletion swiftwave_service/db/migrations/atlas.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
h1:dFMxl02tstPc5Nmao8FA3S7sM7mIXqsSy34VElMP2gk=
h1:SwRv04ZCBJM2kVRlT9CroRjbfMlf4sOror5mPG2X/EM=
20240413191732_init.down.sql h1:HoitObGwuKF/akF4qg3dol2FfNTLCEuf6wHYDuCez8I=
20240413191732_init.up.sql h1:USKdQx/yTz1KJ0+mDwYGhKm3WzX7k+I9+6B6SxImwaE=
20240414051823_server_custom_ssh_port_added.down.sql h1:IC1DFQBQceTPTRdZOo5/WqytH+ZbgcKrQuMCkhArF/0=
Expand Down Expand Up @@ -47,3 +47,5 @@ h1:dFMxl02tstPc5Nmao8FA3S7sM7mIXqsSy34VElMP2gk=
20240628175617_add_extra_fields_in_app_group.up.sql h1:+qBOQc/2bhG1igFdUbWSsZEy01aORuPyBPvxKVXJJoA=
20240906153014_add_hostname_in_application.down.sql h1:tFY94wo3G+UYA51UYt9jbfwwPTS/hCd3pG2F7smUEB8=
20240906153014_add_hostname_in_application.up.sql h1:JAhs73vgSIUzt0l8M8ltRp98dVkwL5lXrdkfHvJ+arE=
20250213190430_test.down.sql h1:ra8BJ92iaL0/Kc7MThF+wzbM1/szBVKHxJUWLq1hf5o=
20250213190430_test.up.sql h1:EDgRcbJknAyddUQ9X3+uGw/MNDilVWHRKcC+9Xuxuxg=
1 change: 1 addition & 0 deletions swiftwave_service/db_models_loader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func main() {
&core.Server{},
&core.ServerLog{},
&core.User{},
&core.UserSession{},
&core.Domain{},
&core.RedirectRule{},
&core.PersistentVolume{},
Expand Down
64 changes: 64 additions & 0 deletions swiftwave_service/graphql/authentication.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading