Skip to content
Open
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 docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ services:
PORT: 8080
TOKEN_HOUR_LIFESPAN: 1
REFRESH_TOKEN_HOUR_LIFESPAN: 720
READ_API_AUTHENTICATION_ENABLED: false
READ_API_AUTHENTICATION_ENABLED: "false"
ports:
- "8080:8080"
depends_on:
Expand Down
20 changes: 16 additions & 4 deletions pkg/api/licenses.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,9 @@ func CreateLicense(c *gin.Context) {
}
// Send notification email about license creation
if email.Email != nil {
email.NotifyLicenseCreated(*lic.User.UserEmail, *lic.User.UserName, *lic.Shortname)
if lic.User.UserEmail != nil && lic.User.UserName != nil {
email.NotifyLicenseCreated(*lic.User.UserEmail, *lic.User.UserName, *lic.Shortname)
}
} else {
logger.LogInfo("Email service is not enabled; skipping notification email sending")
}
Expand Down Expand Up @@ -488,7 +490,9 @@ func UpdateLicense(c *gin.Context) {
}
// Send notification email about license update
if email.Email != nil {
email.NotifyLicenseUpdated(*newLicense.User.UserEmail, *newLicense.User.UserName, *newLicense.Shortname)
if newLicense.User.UserEmail != nil && newLicense.User.UserName != nil {
email.NotifyLicenseUpdated(*newLicense.User.UserEmail, *newLicense.User.UserName, *newLicense.Shortname)
}
} else {
logger.LogInfo("Email service is not enabled; skipping notification email sending")
}
Expand Down Expand Up @@ -852,14 +856,22 @@ func getSimilarLicenses(c *gin.Context) {
return
}
var results []models.SimilarLicense
utils.SetSimilarityThreshold()
threshold := utils.GetSimilarityThreshold()
query := `
SELECT rf_id, rf_shortname, rf_text, similarity(rf_text, ?) AS similarity
FROM license_dbs
WHERE rf_text % ?
ORDER BY similarity DESC
`
if err := db.DB.Raw(query, req.Text, req.Text).Scan(&results).Error; err != nil {
// SET LOCAL scopes the threshold to this transaction only, preventing race conditions
// when multiple requests adjust the threshold concurrently (unlike the previous SET which
// was session-scoped and leaked across concurrent connections in the pool).
if err := db.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Exec(fmt.Sprintf("SET LOCAL pg_trgm.similarity_threshold = %.4f", threshold)).Error; err != nil {
return err
}
return tx.Raw(query, req.Text, req.Text).Scan(&results).Error
}); err != nil {
c.JSON(http.StatusBadRequest, models.LicenseError{
Status: http.StatusBadRequest,
Message: "Database query failed",
Expand Down
14 changes: 11 additions & 3 deletions pkg/api/obligations.go
Original file line number Diff line number Diff line change
Expand Up @@ -1034,14 +1034,22 @@ func getSimilarObligations(c *gin.Context) {
return
}
var results []models.SimilarObligation
utils.SetSimilarityThreshold()
threshold := utils.GetSimilarityThreshold()
rawQuery := `
SELECT id, topic,text, similarity(text, ?) AS similarity
SELECT id, topic, text, similarity(text, ?) AS similarity
FROM obligations
WHERE text % ?
ORDER BY similarity DESC
`
if err := db.DB.Raw(rawQuery, req.Text, req.Text).Scan(&results).Error; err != nil {
// SET LOCAL scopes the threshold to this transaction only, preventing race conditions
// when multiple requests adjust the threshold concurrently (unlike the previous SET which
// was session-scoped and leaked across concurrent connections in the pool).
if err := db.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Exec(fmt.Sprintf("SET LOCAL pg_trgm.similarity_threshold = %.4f", threshold)).Error; err != nil {
return err
}
return tx.Raw(rawQuery, req.Text, req.Text).Scan(&results).Error
}); err != nil {
er := models.LicenseError{
Status: http.StatusBadRequest,
Message: "Database query failed",
Expand Down
12 changes: 6 additions & 6 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,15 +716,15 @@ func DeleteUser(c *gin.Context) {
// @Security ApiKeyAuth
// @Router /users [get]
func GetAllUser(c *gin.Context) {
active, err := strconv.ParseBool(c.Query("active"))
if err != nil {
active = false
}

var users []models.User
query := db.DB.Model(&models.User{})
_ = utils.PreparePaginateResponse(c, query, &models.UserResponse{})
if err := query.Where(&models.User{Active: &active}).Find(&users).Error; err != nil {
if activeStr := c.Query("active"); activeStr != "" {
if active, err := strconv.ParseBool(activeStr); err == nil {
query = query.Where(&models.User{Active: &active})
}
}
if err := query.Find(&users).Error; err != nil {
er := models.LicenseError{
Status: http.StatusNotFound,
Message: "Users not found",
Expand Down
3 changes: 2 additions & 1 deletion pkg/models/licenses.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package models
import (
"encoding/json"
"errors"
"log"
"strconv"
"time"

Expand Down Expand Up @@ -252,7 +253,7 @@ func (dto *LicenseImportDTO) ConvertToLicenseDB() LicenseDB {
bytes, _ := json.Marshal(dto.ExternalRef)

if err := json.Unmarshal(bytes, &ext); err != nil {
panic(err)
log.Printf("failed to unmarshal external_ref for license %v: %v", dto.Shortname, err)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zap logger must be used and in this implementation, function won't stop execution after an error

}

l.ExternalRef = datatypes.NewJSONType(ext)
Expand Down
15 changes: 8 additions & 7 deletions pkg/models/obligations.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"

"github.com/google/uuid"
"gorm.io/datatypes"
Expand Down Expand Up @@ -67,7 +68,7 @@ func (o *Obligation) BeforeCreate(tx *gorm.DB) (err error) {
}
}
}
if o.Type.Id.String() == "" {
if o.Type.Id == uuid.Nil || o.Type.Id.String() == "" {
return fmt.Errorf("obligation type must be one of the following values:%s", allTypes)
}
} else {
Expand All @@ -88,7 +89,7 @@ func (o *Obligation) BeforeCreate(tx *gorm.DB) (err error) {
}
}
}
if o.Classification.Id.String() == "" {
if o.Classification.Id == uuid.Nil || o.Classification.Id.String() == "" {
return fmt.Errorf("obligation classification must be one of the following values:%s", allClassifications)
}
} else {
Expand All @@ -109,7 +110,7 @@ func (o *Obligation) BeforeCreate(tx *gorm.DB) (err error) {
}
}
}
if o.Classification.Id.String() == "" {
if o.Category.Id == uuid.Nil || o.Category.Id.String() == "" {
return fmt.Errorf("obligation category must be one of the following values:%s", allCategories)
}
} else {
Expand Down Expand Up @@ -138,7 +139,7 @@ func (o *Obligation) BeforeUpdate(tx *gorm.DB) (err error) {
}
}
}
if o.Type.Id.String() == "" {
if o.Type.Id == uuid.Nil || o.Type.Id.String() == "" {
return fmt.Errorf("obligation type must be one of the following values:%s", allTypes)
}
}
Expand All @@ -159,7 +160,7 @@ func (o *Obligation) BeforeUpdate(tx *gorm.DB) (err error) {
}
}
}
if o.Classification.Id.String() == "" {
if o.Classification.Id == uuid.Nil || o.Classification.Id.String() == "" {
return fmt.Errorf("obligation classification must be one of the following values:%s", allClassifications)
}
}
Expand All @@ -177,7 +178,7 @@ func (o *Obligation) BeforeUpdate(tx *gorm.DB) (err error) {
}
}
}
if o.Classification.Id.String() == "" {
if o.Category.Id == uuid.Nil || o.Category.Id.String() == "" {
return fmt.Errorf("obligation category must be one of the following values:%s", allCategories)
}
}
Expand Down Expand Up @@ -336,7 +337,7 @@ func (obDto *ObligationFileDTO) ConvertToObligation() Obligation {
bytes, _ := json.Marshal(obDto.ExternalRef)

if err := json.Unmarshal(bytes, &ext); err != nil {
panic(err)
log.Printf("failed to unmarshal external_ref for obligation %v: %v", obDto.Topic, err)
}

o.ExternalRef = datatypes.NewJSONType(ext)
Expand Down
33 changes: 13 additions & 20 deletions pkg/utils/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,26 +672,19 @@ func Populatedb(datafile string) {
}
}

// SetSimilarityThreshold parses the env var and sets the threshold in Postgres.
func SetSimilarityThreshold() {
defaultThreshold := 0.7
thresholdStr := os.Getenv("SIMILARITY_THRESHOLD")

threshold := defaultThreshold
if thresholdStr != "" {
if parsed, err := strconv.ParseFloat(thresholdStr, 64); err == nil {
threshold = parsed
} else {
log.Printf("Invalid SIMILARITY_THRESHOLD '%s', using default %.1f", thresholdStr, defaultThreshold)
}
} else {
log.Printf("SIMILARITY_THRESHOLD not set, using default %.1f", defaultThreshold)
}

query := fmt.Sprintf("SET pg_trgm.similarity_threshold = %f", threshold)
if err := db.DB.Exec(query).Error; err != nil {
log.Println("Failed to set similarity threshold:", err)
// GetSimilarityThreshold parses the env var and returns the threshold value.
func GetSimilarityThreshold() float64 {
const defaultThreshold = 0.7
thresholdStr := strings.TrimSpace(os.Getenv("SIMILARITY_THRESHOLD"))
if thresholdStr == "" {
return defaultThreshold
}
parsed, err := strconv.ParseFloat(thresholdStr, 64)
if err != nil {
log.Printf("Invalid SIMILARITY_THRESHOLD '%s', using default %.1f", thresholdStr, defaultThreshold)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zap logger must be used

return defaultThreshold
}
return parsed
}

// GetAuditEntity is an utility function to fetch obligation or license associated with an audit
Expand Down Expand Up @@ -792,7 +785,7 @@ func GetKid(token string) (string, error) {

parts := strings.Split(token, ".")

decodedBytes, err := base64.StdEncoding.DecodeString(parts[0])
decodedBytes, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return "", err
}
Expand Down
Loading