Files
paralus/pkg/service/organization.go
Swastik Gour 7a4895a5b2 updated main.go script to reinitialize the permissions when the are e… (#366)
* updated main.go script to reinitialize the permissions when the are existing or there is any update

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* added few corrections and added better error logging

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* added the checks for checking the existence of different components

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* started adding the Upsert function

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* added upsert to role.go

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* added tests and auditing

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* updated packages

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* updated go version

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* added minor changes

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* added correction to role_test.go

Signed-off-by: swastik959 <Sswastik959@gmail.com>

* corrected test cases

Signed-off-by: swastik959 <Sswastik959@gmail.com>

---------

Signed-off-by: swastik959 <Sswastik959@gmail.com>
2025-03-20 16:15:38 +05:30

457 lines
15 KiB
Go

package service
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/paralus/paralus/internal/dao"
"github.com/paralus/paralus/internal/models"
commonv3 "github.com/paralus/paralus/proto/types/commonpb/v3"
v3 "github.com/paralus/paralus/proto/types/commonpb/v3"
systemv3 "github.com/paralus/paralus/proto/types/systempb/v3"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
const (
organizationKind = "Organization"
organizationListKind = "OrganizationList"
)
// OrganizationService is the interface for organization operations
type OrganizationService interface {
// create organization
Create(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error)
// get organization by id
GetByID(ctx context.Context, id string) (*systemv3.Organization, error)
// get organization by id
GetByName(ctx context.Context, name string) (*systemv3.Organization, error)
// create or update organization
Update(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error)
// delete organization
Delete(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error)
// list organization
List(ctx context.Context, organization *systemv3.Organization) (*systemv3.OrganizationList, error)
// Upsert Organization
Upsert(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error)
}
// organizationService implements OrganizationService
type organizationService struct {
db *bun.DB
al *zap.Logger
}
// NewOrganizationService return new organization service
func NewOrganizationService(db *bun.DB, al *zap.Logger) OrganizationService {
return &organizationService{db, al}
}
func (s *organizationService) Create(ctx context.Context, org *systemv3.Organization) (*systemv3.Organization, error) {
var partner models.Partner
_, err := dao.GetByName(ctx, s.db, org.Metadata.Partner, &partner)
if err != nil {
return &systemv3.Organization{}, err
}
//update default organization setting values
org.Spec.Settings = &systemv3.OrganizationSettings{
Lockout: &systemv3.Lockout{
Enabled: true,
PeriodMin: 15,
Attempts: 5,
},
IdleLogoutMin: 60,
}
sb, err := json.MarshalIndent(org.GetSpec().GetSettings(), "", "\t")
if err != nil {
return &systemv3.Organization{}, err
}
//convert v3 spec to internal models
organization := models.Organization{
Name: org.GetMetadata().GetName(),
Description: org.GetMetadata().GetDescription(),
Trash: false,
Settings: json.RawMessage(sb),
BillingAddress: org.GetSpec().GetBillingAddress(),
PartnerId: partner.ID,
Active: org.GetSpec().GetActive(),
Approved: org.GetSpec().GetApproved(),
Type: org.GetSpec().GetType(),
AddressLine1: org.GetSpec().GetAddressLine1(),
AddressLine2: org.GetSpec().GetAddressLine2(),
City: org.GetSpec().GetCity(),
Country: org.GetSpec().GetCountry(),
Phone: org.GetSpec().GetPhone(),
State: org.GetSpec().GetState(),
Zipcode: org.GetSpec().GetZipcode(),
IsPrivate: org.GetSpec().GetIsPrivate(),
IsTOTPEnabled: org.GetSpec().GetIsTotpEnabled(),
AreClustersShared: org.GetSpec().GetAreClustersShared(),
CreatedAt: time.Now(),
ModifiedAt: time.Now(),
}
entity, err := dao.Create(ctx, s.db, &organization)
if err != nil {
return &systemv3.Organization{}, err
}
if createdOrg, ok := entity.(*models.Organization); ok {
//update v3 spec
org.Metadata.Id = createdOrg.ID.String()
CreateOrganizationAuditEvent(ctx, s.al, AuditActionCreate, org.GetMetadata().GetName(), createdOrg.ID, nil, org.GetSpec().GetSettings())
}
return org, nil
}
func (s *organizationService) GetByID(ctx context.Context, id string) (*systemv3.Organization, error) {
organization := &systemv3.Organization{
ApiVersion: apiVersion,
Kind: organizationKind,
Metadata: &v3.Metadata{
Id: id,
},
Spec: &systemv3.OrganizationSpec{},
}
uid, err := uuid.Parse(id)
if err != nil {
return &systemv3.Organization{}, err
}
entity, err := dao.GetByID(ctx, s.db, uid, &models.Organization{})
if err != nil {
return &systemv3.Organization{}, err
}
if org, ok := entity.(*models.Organization); ok {
var partner models.Partner
_, err := dao.GetByID(ctx, s.db, org.PartnerId, &partner)
if err != nil {
return &systemv3.Organization{}, err
}
organization, err = prepareOrganizationResponse(organization, org, partner.Name)
if err != nil {
return &systemv3.Organization{}, err
}
return organization, nil
} else {
organization := &systemv3.Organization{
ApiVersion: apiVersion,
Kind: organizationKind,
Status: &v3.Status{
ConditionType: "Describe",
ConditionStatus: v3.ConditionStatus_StatusNotSet,
Reason: "Unable to fetch organization information",
LastUpdated: timestamppb.Now(),
},
}
return organization, nil
}
}
func (s *organizationService) GetByName(ctx context.Context, name string) (*systemv3.Organization, error) {
organization := &systemv3.Organization{
ApiVersion: apiVersion,
Kind: organizationKind,
Metadata: &v3.Metadata{
Name: name,
},
}
entity, err := dao.GetByName(ctx, s.db, name, &models.Organization{})
if err != nil {
return &systemv3.Organization{}, err
}
if org, ok := entity.(*models.Organization); ok {
var partner models.Partner
_, err := dao.GetByID(ctx, s.db, org.PartnerId, &partner)
if err != nil {
return &systemv3.Organization{}, err
}
organization, err = prepareOrganizationResponse(organization, org, partner.Name)
if err != nil {
return &systemv3.Organization{}, err
}
}
return organization, nil
}
func (s *organizationService) Update(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error) {
entity, err := dao.GetByName(ctx, s.db, organization.Metadata.Name, &models.Organization{})
if err != nil {
return &systemv3.Organization{}, err
}
if org, ok := entity.(*models.Organization); ok {
settingsAfter := organization.GetSpec().GetSettings()
settingsBefore := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &settingsBefore) // ignore any unmarshelling issues
sb, err := json.MarshalIndent(settingsAfter, "", "\t")
if err != nil {
return &systemv3.Organization{}, err
}
//update organization details
org.Name = organization.GetMetadata().GetName()
org.Description = organization.GetMetadata().GetDescription()
org.ModifiedAt = time.Now()
org.Trash = false
org.Settings = json.RawMessage(sb)
org.BillingAddress = organization.GetSpec().GetBillingAddress()
org.Active = organization.GetSpec().GetActive()
org.Approved = organization.GetSpec().GetApproved()
org.Type = organization.GetSpec().GetType()
org.AddressLine1 = organization.GetSpec().GetAddressLine1()
org.AddressLine2 = organization.GetSpec().GetAddressLine2()
org.City = organization.GetSpec().GetCity()
org.Country = organization.GetSpec().GetCountry()
org.Phone = organization.GetSpec().GetPhone()
org.State = organization.GetSpec().GetState()
org.Zipcode = organization.GetSpec().GetZipcode()
org.IsPrivate = organization.GetSpec().GetIsPrivate()
org.IsTOTPEnabled = organization.GetSpec().GetIsTotpEnabled()
org.AreClustersShared = organization.GetSpec().GetAreClustersShared()
_, err = dao.Update(ctx, s.db, org.ID, org)
if err != nil {
return &systemv3.Organization{}, err
}
CreateOrganizationAuditEvent(ctx, s.al, AuditActionUpdate, organization.GetMetadata().GetName(), org.ID, &settingsBefore, settingsAfter)
}
return organization, nil
}
func (s *organizationService) Delete(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error) {
entity, err := dao.GetByName(ctx, s.db, organization.Metadata.Name, &models.Organization{})
if err != nil {
return &systemv3.Organization{}, err
}
if org, ok := entity.(*models.Organization); ok {
err := dao.DeleteR(ctx, s.db, org.ID, org)
if err != nil {
return &systemv3.Organization{}, err
}
//update v3 status
organization.Metadata.Name = org.Name
organization.Metadata.ModifiedAt = timestamppb.New(org.ModifiedAt)
orgSettings := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &orgSettings) // ignore any unmarshelling issues
CreateOrganizationAuditEvent(ctx, s.al, AuditActionDelete, organization.GetMetadata().GetName(), org.ID, &orgSettings, nil)
}
return organization, nil
}
func (s *organizationService) List(ctx context.Context, organization *systemv3.Organization) (*systemv3.OrganizationList, error) {
var organizations []*systemv3.Organization
organinzationList := &systemv3.OrganizationList{
ApiVersion: apiVersion,
Kind: organizationListKind,
Metadata: &v3.ListMetadata{
Count: 0,
},
}
if len(organization.Metadata.Partner) > 0 {
var partner models.Partner
_, err := dao.GetByName(ctx, s.db, organization.Metadata.Partner, &partner)
if err != nil {
return &systemv3.OrganizationList{}, err
}
var orgs []models.Organization
entities, err := dao.List(ctx, s.db, uuid.NullUUID{UUID: partner.ID, Valid: true}, uuid.NullUUID{UUID: uuid.Nil}, &orgs)
if err != nil {
return &systemv3.OrganizationList{}, err
}
if orgs, ok := entities.(*[]models.Organization); ok {
for _, org := range *orgs {
var settings systemv3.OrganizationSettings
err := json.Unmarshal(org.Settings, &settings)
if err != nil {
return &systemv3.OrganizationList{}, err
}
organization.Metadata = &v3.Metadata{
Name: org.Name,
Description: org.Description,
Partner: partner.Name,
Id: org.ID.String(),
ModifiedAt: timestamppb.New(org.ModifiedAt),
}
organization.Spec = &systemv3.OrganizationSpec{
BillingAddress: org.BillingAddress,
Active: org.Active,
Approved: org.Approved,
Type: org.Type,
AddressLine1: org.AddressLine1,
AddressLine2: org.AddressLine2,
City: org.City,
Country: org.Country,
Phone: org.Phone,
State: org.State,
Zipcode: org.Zipcode,
IsPrivate: org.IsPrivate,
IsTotpEnabled: org.IsTOTPEnabled,
AreClustersShared: org.AreClustersShared,
Settings: &settings,
}
organizations = append(organizations, organization)
}
//update the list metadata and items response
organinzationList.Metadata = &v3.ListMetadata{
Count: int64(len(organizations)),
}
organinzationList.Items = organizations
}
} else {
return organinzationList, fmt.Errorf("missing partner in metadata")
}
return organinzationList, nil
}
func prepareOrganizationResponse(organization *systemv3.Organization, org *models.Organization, partnerName string) (*systemv3.Organization, error) {
var settings systemv3.OrganizationSettings
if org.Settings != nil {
err := json.Unmarshal(org.Settings, &settings)
if err != nil {
return &systemv3.Organization{}, err
}
}
organization.Metadata = &v3.Metadata{
Name: org.Name,
Id: org.ID.String(),
Description: org.Description,
Partner: partnerName,
ModifiedAt: timestamppb.New(org.ModifiedAt),
}
organization.Spec = &systemv3.OrganizationSpec{
BillingAddress: org.BillingAddress,
Active: org.Active,
Approved: org.Approved,
Type: org.Type,
AddressLine1: org.AddressLine1,
AddressLine2: org.AddressLine2,
City: org.City,
Country: org.Country,
Phone: org.Phone,
State: org.State,
Zipcode: org.Zipcode,
IsPrivate: org.IsPrivate,
IsTotpEnabled: org.IsTOTPEnabled,
AreClustersShared: org.AreClustersShared,
Settings: &settings,
}
return organization, nil
}
func (s *organizationService) Upsert(ctx context.Context, organization *systemv3.Organization) (*systemv3.Organization, error) {
// First get the partner
var partner models.Partner
_, err := dao.GetByName(ctx, s.db, organization.GetMetadata().GetPartner(), &partner)
if err != nil {
return nil, fmt.Errorf("failed to get partner: %v", err)
}
sb, err := json.Marshal(map[string]interface{}{})
if err != nil {
return nil, fmt.Errorf("failed to marshal settings: %v", err)
}
org := models.Organization{
Name: organization.GetMetadata().GetName(),
Description: organization.GetMetadata().GetDescription(),
Trash: false,
Settings: json.RawMessage(sb),
BillingAddress: organization.GetSpec().GetBillingAddress(),
PartnerId: partner.ID, // Now using the correct partner ID
Active: organization.GetSpec().GetActive(),
Approved: organization.GetSpec().GetApproved(),
Type: organization.GetSpec().GetType(),
AddressLine1: organization.GetSpec().GetAddressLine1(),
AddressLine2: organization.GetSpec().GetAddressLine2(),
City: organization.GetSpec().GetCity(),
Country: organization.GetSpec().GetCountry(),
Phone: organization.GetSpec().GetPhone(),
State: organization.GetSpec().GetState(),
Zipcode: organization.GetSpec().GetZipcode(),
IsPrivate: organization.GetSpec().GetIsPrivate(),
IsTOTPEnabled: organization.GetSpec().GetIsTotpEnabled(),
AreClustersShared: organization.GetSpec().GetAreClustersShared(),
CreatedAt: time.Now(),
ModifiedAt: time.Now(),
}
_, err = s.db.NewInsert().
Model(&org).
On("CONFLICT (name, partner_id) DO UPDATE").
Set("description = EXCLUDED.description").
Set("modified_at = EXCLUDED.modified_at").
Set("trash = EXCLUDED.trash").
Set("settings = EXCLUDED.settings").
Set("billing_address = EXCLUDED.billing_address").
Set("active = EXCLUDED.active").
Set("approved = EXCLUDED.approved").
Set("type = EXCLUDED.type").
Set("address_line1 = EXCLUDED.address_line1").
Set("address_line2 = EXCLUDED.address_line2").
Set("city = EXCLUDED.city").
Set("country = EXCLUDED.country").
Set("phone = EXCLUDED.phone").
Set("state = EXCLUDED.state").
Set("zipcode = EXCLUDED.zipcode").
Set("is_totp_enabled = EXCLUDED.is_totp_enabled").
Set("are_clusters_shared = EXCLUDED.are_clusters_shared").
Set("psps_enabled = EXCLUDED.psps_enabled").
Set("custom_psps_enabled = EXCLUDED.custom_psps_enabled").
Set("default_blueprints_enabled = EXCLUDED.default_blueprints_enabled").
Exec(ctx)
if err != nil {
return nil, fmt.Errorf("failed to upsert organization: %v", err)
}
orgSettings := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &orgSettings)
CreateOrganizationAuditEvent(ctx, s.al, AuditActionUpsert, organization.GetMetadata().GetName(), org.ID, nil, &orgSettings)
return &systemv3.Organization{
Metadata: &commonv3.Metadata{
Name: org.Name,
Description: org.Description,
Partner: partner.Name,
},
Spec: &systemv3.OrganizationSpec{
Active: org.Active,
},
}, nil
}