mirror of https://github.com/h44z/wg-portal.git
				
				
				
			Option to limit peer count that a normal user can create (#457)
This commit is contained in:
		
							parent
							
								
									cbf8c5bca9
								
							
						
					
					
						commit
						f0be66aea4
					
				|  | @ -38,6 +38,7 @@ advanced: | ||||||
|   rule_prio_offset: 20000 |   rule_prio_offset: 20000 | ||||||
|   route_table_offset: 20000 |   route_table_offset: 20000 | ||||||
|   api_admin_only: true |   api_admin_only: true | ||||||
|  |   limit_additional_user_peers: 0 | ||||||
| 
 | 
 | ||||||
| database: | database: | ||||||
|   debug: false |   debug: false | ||||||
|  | @ -215,6 +216,10 @@ Additional or more specialized configuration options for logging and interface c | ||||||
| - **Default:** `true` | - **Default:** `true` | ||||||
| - **Description:** If `true`, the public REST API is accessible only to admin users. The API docs live at [`/api/v1/doc.html`](../rest-api/api-doc.md). | - **Description:** If `true`, the public REST API is accessible only to admin users. The API docs live at [`/api/v1/doc.html`](../rest-api/api-doc.md). | ||||||
| 
 | 
 | ||||||
|  | ### `limit_additional_user_peers` | ||||||
|  | - **Default:** `0` | ||||||
|  | - **Description:** Limit additional peers a normal user can create. `0` means unlimited. | ||||||
|  | 
 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| ## Database | ## Database | ||||||
|  |  | ||||||
|  | @ -188,6 +188,30 @@ func (m Manager) CreatePeer(ctx context.Context, peer *domain.Peer) (*domain.Pee | ||||||
| 
 | 
 | ||||||
| 	sessionUser := domain.GetUserInfo(ctx) | 	sessionUser := domain.GetUserInfo(ctx) | ||||||
| 
 | 
 | ||||||
|  |     // Enforce peer limit for non-admin users if LimitAdditionalUserPeers is set
 | ||||||
|  |     if m.cfg.Core.SelfProvisioningAllowed && !sessionUser.IsAdmin && m.cfg.Advanced.LimitAdditionalUserPeers > 0 { | ||||||
|  |         peers, err := m.db.GetUserPeers(ctx, peer.UserIdentifier) | ||||||
|  |         if err != nil { | ||||||
|  |             return nil, fmt.Errorf("failed to fetch peers for user %s: %w", peer.UserIdentifier, err) | ||||||
|  |         } | ||||||
|  |         // Count enabled peers (disabled IS NULL)
 | ||||||
|  |         peerCount := 0 | ||||||
|  |         for _, p := range peers { | ||||||
|  |             if !p.IsDisabled() { | ||||||
|  |                 peerCount++ | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         totalAllowedPeers := 1 + m.cfg.Advanced.LimitAdditionalUserPeers // 1 default peer + x additional peers
 | ||||||
|  |         if peerCount >= totalAllowedPeers { | ||||||
|  |             slog.WarnContext(ctx, "peer creation blocked due to limit", | ||||||
|  |                 "user", peer.UserIdentifier, | ||||||
|  |                 "current_count", peerCount, | ||||||
|  |                 "allowed_count", totalAllowedPeers) | ||||||
|  |             return nil, fmt.Errorf("peer limit reached (%d peers allowed): %w", totalAllowedPeers, domain.ErrNoPermission) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 	existingPeer, err := m.db.GetPeer(ctx, peer.Identifier) | 	existingPeer, err := m.db.GetPeer(ctx, peer.Identifier) | ||||||
| 	if err != nil && !errors.Is(err, domain.ErrNotFound) { | 	if err != nil && !errors.Is(err, domain.ErrNotFound) { | ||||||
| 		return nil, fmt.Errorf("unable to load existing peer %s: %w", peer.Identifier, err) | 		return nil, fmt.Errorf("unable to load existing peer %s: %w", peer.Identifier, err) | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ type Config struct { | ||||||
| 		RulePrioOffset           int           `yaml:"rule_prio_offset"` | 		RulePrioOffset           int           `yaml:"rule_prio_offset"` | ||||||
| 		RouteTableOffset         int           `yaml:"route_table_offset"` | 		RouteTableOffset         int           `yaml:"route_table_offset"` | ||||||
| 		ApiAdminOnly             bool          `yaml:"api_admin_only"` // if true, only admin users can access the API
 | 		ApiAdminOnly             bool          `yaml:"api_admin_only"` // if true, only admin users can access the API
 | ||||||
|  | 		LimitAdditionalUserPeers int           `yaml:"limit_additional_user_peers"` | ||||||
| 	} `yaml:"advanced"` | 	} `yaml:"advanced"` | ||||||
| 
 | 
 | ||||||
| 	Statistics struct { | 	Statistics struct { | ||||||
|  | @ -76,6 +77,7 @@ func (c *Config) LogStartupValues() { | ||||||
| 		"reEnablePeerAfterUserEnable", c.Core.ReEnablePeerAfterUserEnable, | 		"reEnablePeerAfterUserEnable", c.Core.ReEnablePeerAfterUserEnable, | ||||||
| 		"deletePeerAfterUserDeleted", c.Core.DeletePeerAfterUserDeleted, | 		"deletePeerAfterUserDeleted", c.Core.DeletePeerAfterUserDeleted, | ||||||
| 		"selfProvisioningAllowed", c.Core.SelfProvisioningAllowed, | 		"selfProvisioningAllowed", c.Core.SelfProvisioningAllowed, | ||||||
|  | 		"limitAdditionalUserPeers", c.Advanced.LimitAdditionalUserPeers, | ||||||
| 		"importExisting", c.Core.ImportExisting, | 		"importExisting", c.Core.ImportExisting, | ||||||
| 		"restoreState", c.Core.RestoreState, | 		"restoreState", c.Core.RestoreState, | ||||||
| 		"useIpV6", c.Advanced.UseIpV6, | 		"useIpV6", c.Advanced.UseIpV6, | ||||||
|  | @ -137,6 +139,7 @@ func defaultConfig() *Config { | ||||||
| 	cfg.Advanced.RulePrioOffset = 20000 | 	cfg.Advanced.RulePrioOffset = 20000 | ||||||
| 	cfg.Advanced.RouteTableOffset = 20000 | 	cfg.Advanced.RouteTableOffset = 20000 | ||||||
| 	cfg.Advanced.ApiAdminOnly = true | 	cfg.Advanced.ApiAdminOnly = true | ||||||
|  | 	cfg.Advanced.LimitAdditionalUserPeers = 0 | ||||||
| 
 | 
 | ||||||
| 	cfg.Statistics.UsePingChecks = true | 	cfg.Statistics.UsePingChecks = true | ||||||
| 	cfg.Statistics.PingCheckWorkers = 10 | 	cfg.Statistics.PingCheckWorkers = 10 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue