feat(backend): analytics server initial setup
This commit is contained in:
parent
c6a55b18a8
commit
de87e1ad16
8 changed files with 125 additions and 0 deletions
|
|
@ -2,6 +2,7 @@ package analytics
|
|||
|
||||
import (
|
||||
"openreplay/backend/internal/config/analytics"
|
||||
"openreplay/backend/pkg/common/api/auth"
|
||||
"openreplay/backend/pkg/db/postgres/pool"
|
||||
"openreplay/backend/pkg/flakeid"
|
||||
"openreplay/backend/pkg/logger"
|
||||
|
|
@ -12,6 +13,7 @@ import (
|
|||
type ServicesBuilder struct {
|
||||
Flaker *flakeid.Flaker
|
||||
ObjStorage objectstorage.ObjectStorage
|
||||
Auth auth.Auth
|
||||
}
|
||||
|
||||
func NewServiceBuilder(log logger.Logger, cfg *analytics.Config, pgconn pool.Pool) (*ServicesBuilder, error) {
|
||||
|
|
@ -23,5 +25,6 @@ func NewServiceBuilder(log logger.Logger, cfg *analytics.Config, pgconn pool.Poo
|
|||
return &ServicesBuilder{
|
||||
Flaker: flaker,
|
||||
ObjStorage: objStore,
|
||||
Auth: auth.NewAuth(log, cfg.JWTSecret, pgconn),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
1
backend/pkg/analytics/service/timeseries.go
Normal file
1
backend/pkg/analytics/service/timeseries.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package service
|
||||
51
backend/pkg/common/api/auth/auth.go
Normal file
51
backend/pkg/common/api/auth/auth.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
"openreplay/backend/pkg/db/postgres/pool"
|
||||
"openreplay/backend/pkg/logger"
|
||||
)
|
||||
|
||||
type Auth interface {
|
||||
IsAuthorized(authHeader string, permissions []string, isExtension bool) (*User, error)
|
||||
}
|
||||
|
||||
type authImpl struct {
|
||||
log logger.Logger
|
||||
secret string
|
||||
pgconn pool.Pool
|
||||
}
|
||||
|
||||
func NewAuth(log logger.Logger, jwtSecret string, conn pool.Pool) Auth {
|
||||
return &authImpl{
|
||||
log: log,
|
||||
secret: jwtSecret,
|
||||
pgconn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func parseJWT(authHeader, secret string) (*JWTClaims, error) {
|
||||
if authHeader == "" {
|
||||
return nil, fmt.Errorf("authorization header missing")
|
||||
}
|
||||
tokenParts := strings.Split(authHeader, "Bearer ")
|
||||
if len(tokenParts) != 2 {
|
||||
return nil, fmt.Errorf("invalid authorization header")
|
||||
}
|
||||
tokenString := tokenParts[1]
|
||||
|
||||
claims := &JWTClaims{}
|
||||
token, err := jwt.ParseWithClaims(tokenString, claims,
|
||||
func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(secret), nil
|
||||
})
|
||||
if err != nil || !token.Valid {
|
||||
fmt.Printf("token err: %v\n", err)
|
||||
return nil, fmt.Errorf("invalid token")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
10
backend/pkg/common/api/auth/authorizer.go
Normal file
10
backend/pkg/common/api/auth/authorizer.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package auth
|
||||
|
||||
func (a *authImpl) IsAuthorized(authHeader string, permissions []string, isExtension bool) (*User, error) {
|
||||
secret := a.secret
|
||||
jwtInfo, err := parseJWT(authHeader, secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return authUser(a.pgconn, jwtInfo.UserId, jwtInfo.TenantID, int(jwtInfo.IssuedAt.Unix()), isExtension)
|
||||
}
|
||||
34
backend/pkg/common/api/auth/model.go
Normal file
34
backend/pkg/common/api/auth/model.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package auth
|
||||
|
||||
import "github.com/golang-jwt/jwt/v5"
|
||||
|
||||
type JWTClaims struct {
|
||||
UserId int `json:"userId"`
|
||||
TenantID int `json:"tenantId"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
TenantID uint64 `json:"tenantId"`
|
||||
JwtIat int `json:"jwtIat"`
|
||||
Permissions map[string]bool `json:"permissions"`
|
||||
AuthMethod string
|
||||
}
|
||||
|
||||
func (u *User) HasPermission(perm string) bool {
|
||||
if u.Permissions == nil {
|
||||
return true // no permissions
|
||||
}
|
||||
_, ok := u.Permissions[perm]
|
||||
return ok
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
22
backend/pkg/common/api/auth/storage.go
Normal file
22
backend/pkg/common/api/auth/storage.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"openreplay/backend/pkg/db/postgres/pool"
|
||||
)
|
||||
|
||||
func authUser(conn pool.Pool, userID, tenantID, jwtIAT int, isExtension bool) (*User, error) {
|
||||
sql := `
|
||||
SELECT user_id, name, email
|
||||
FROM public.users
|
||||
WHERE user_id = $1 AND deleted_at IS NULL
|
||||
LIMIT 1;`
|
||||
user := &User{TenantID: 1, AuthMethod: "jwt"}
|
||||
if err := conn.QueryRow(sql, userID).Scan(&user.ID, &user.Name, &user.Email, &user.JwtIat); err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
if user.JwtIat == 0 || abs(jwtIAT-user.JwtIat) > 1 {
|
||||
return nil, fmt.Errorf("token has been updated")
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
3
backend/pkg/common/api/service/user.go
Normal file
3
backend/pkg/common/api/service/user.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package service
|
||||
|
||||
var getUserSQL = `SELECT 1, name, email FROM public.users WHERE user_id = $1 AND deleted_at IS NULL LIMIT 1`
|
||||
1
backend/pkg/common/builder.go
Normal file
1
backend/pkg/common/builder.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package common
|
||||
Loading…
Add table
Reference in a new issue