Added clients page filter by subnet range
This commit is contained in:
		
							parent
							
								
									53eaab0079
								
							
						
					
					
						commit
						2027b3fa5d
					
				|  | @ -18,6 +18,11 @@ function renderClientList(data) { | ||||||
|             allowedIpsHtml += `<small class="badge badge-secondary">${obj}</small> `; |             allowedIpsHtml += `<small class="badge badge-secondary">${obj}</small> `; | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|  |         let subnetRangesString = ""; | ||||||
|  |         if (obj.Client.subnet_ranges && obj.Client.subnet_ranges.length > 0) { | ||||||
|  |             subnetRangesString = obj.Client.subnet_ranges.join(',') | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // render client html content
 |         // render client html content
 | ||||||
|         let html = `<div class="col-sm-6 col-md-6 col-lg-4" id="client_${obj.Client.id}">
 |         let html = `<div class="col-sm-6 col-md-6 col-lg-4" id="client_${obj.Client.id}">
 | ||||||
|                         <div class="info-box"> |                         <div class="info-box"> | ||||||
|  | @ -59,6 +64,7 @@ function renderClientList(data) { | ||||||
|                                 <hr> |                                 <hr> | ||||||
|                                 <span class="info-box-text"><i class="fas fa-user"></i> ${obj.Client.name}</span> |                                 <span class="info-box-text"><i class="fas fa-user"></i> ${obj.Client.name}</span> | ||||||
|                                 <span class="info-box-text" style="display: none"><i class="fas fa-key"></i> ${obj.Client.public_key}</span> |                                 <span class="info-box-text" style="display: none"><i class="fas fa-key"></i> ${obj.Client.public_key}</span> | ||||||
|  |                                 <span class="info-box-text" style="display: none"><i class="fas fa-subnetrange"></i>${subnetRangesString}</span> | ||||||
|                                 <span class="info-box-text"><i class="fas fa-envelope"></i> ${obj.Client.email}</span> |                                 <span class="info-box-text"><i class="fas fa-envelope"></i> ${obj.Client.email}</span> | ||||||
|                                 <span class="info-box-text"><i class="fas fa-clock"></i> |                                 <span class="info-box-text"><i class="fas fa-clock"></i> | ||||||
|                                     ${prettyDateTime(obj.Client.created_at)}</span> |                                     ${prettyDateTime(obj.Client.created_at)}</span> | ||||||
|  |  | ||||||
|  | @ -366,6 +366,10 @@ func GetClients(db store.IStore) echo.HandlerFunc { | ||||||
| 			}) | 			}) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		for i, clientData := range clientDataList { | ||||||
|  | 			clientDataList[i] = util.FillClientSubnetRange(clientData) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		return c.JSON(http.StatusOK, clientDataList) | 		return c.JSON(http.StatusOK, clientDataList) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -391,7 +395,7 @@ func GetClient(db store.IStore) echo.HandlerFunc { | ||||||
| 			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"}) | 			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"}) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return c.JSON(http.StatusOK, clientData) | 		return c.JSON(http.StatusOK, util.FillClientSubnetRange(clientData)) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ type Client struct { | ||||||
| 	PresharedKey    string    `json:"preshared_key"` | 	PresharedKey    string    `json:"preshared_key"` | ||||||
| 	Name            string    `json:"name"` | 	Name            string    `json:"name"` | ||||||
| 	Email           string    `json:"email"` | 	Email           string    `json:"email"` | ||||||
|  | 	SubnetRanges    []string  `json:"subnet_ranges,omitempty"` | ||||||
| 	AllocatedIPs    []string  `json:"allocated_ips"` | 	AllocatedIPs    []string  `json:"allocated_ips"` | ||||||
| 	AllowedIPs      []string  `json:"allowed_ips"` | 	AllowedIPs      []string  `json:"allowed_ips"` | ||||||
| 	ExtraAllowedIPs []string  `json:"extra_allowed_ips"` | 	ExtraAllowedIPs []string  `json:"extra_allowed_ips"` | ||||||
|  | @ -29,7 +30,7 @@ type ClientData struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type QRCodeSettings struct { | type QRCodeSettings struct { | ||||||
| 	Enabled       bool | 	Enabled    bool | ||||||
| 	IncludeDNS    bool | 	IncludeDNS bool | ||||||
| 	IncludeMTU    bool | 	IncludeMTU bool | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -58,11 +58,13 @@ | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="form-group form-group-sm"> |                 <div class="form-group form-group-sm"> | ||||||
|                     <select name="status-selector" id="status-selector" class="custom-select form-control-navbar" style="margin-left: 0.5em; height: 90%; font-size: 14px;"> |                     <select name="status-selector" id="status-selector" class="custom-select form-control-navbar" style="margin-left: 0.5em; height: 90%; font-size: 14px;"> | ||||||
|  |                         <!-- SEE updateSearchList() in clients.html BEFORE EDITING --> | ||||||
|                         <option value="All">All</option> |                         <option value="All">All</option> | ||||||
|                         <option value="Enabled">Enabled</option> |                         <option value="Enabled">Enabled</option> | ||||||
|                         <option value="Disabled">Disabled</option> |                         <option value="Disabled">Disabled</option> | ||||||
|                         <option value="Connected">Connected</option> |                         <option value="Connected">Connected</option> | ||||||
|                         <option value="Disconnected">Disconnected</option> |                         <option value="Disconnected">Disconnected</option> | ||||||
|  |                         <!-- SEE updateSearchList() in clients.html BEFORE EDITING --> | ||||||
|                     </select> |                     </select> | ||||||
|                 </div> |                 </div> | ||||||
|             </form> |             </form> | ||||||
|  | @ -210,7 +212,7 @@ | ||||||
|                                 <input type="text" class="form-control" id="client_email" name="client_email"> |                                 <input type="text" class="form-control" id="client_email" name="client_email"> | ||||||
|                             </div> |                             </div> | ||||||
|                             <div class="form-group"> |                             <div class="form-group"> | ||||||
|                                 <label for="subnet_ranges" class="control-label">Subnet ranges</label> |                                 <label for="subnet_ranges" class="control-label">Subnet range</label> | ||||||
|                                 <select id="subnet_ranges" class="select2" |                                 <select id="subnet_ranges" class="select2" | ||||||
|                                     data-placeholder="Select a subnet range" style="width: 100%;"> |                                     data-placeholder="Select a subnet range" style="width: 100%;"> | ||||||
|                                 </select> |                                 </select> | ||||||
|  | @ -374,7 +376,6 @@ | ||||||
| 
 | 
 | ||||||
|         $(document).ready(function () { |         $(document).ready(function () { | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|             addGlobalStyle(` |             addGlobalStyle(` | ||||||
|                 .toast-top-right-fix { |                 .toast-top-right-fix { | ||||||
|                     top: 67px; |                     top: 67px; | ||||||
|  | @ -387,6 +388,8 @@ | ||||||
|             toastr.options.positionClass = 'toast-top-right-fix'; |             toastr.options.positionClass = 'toast-top-right-fix'; | ||||||
| 
 | 
 | ||||||
|             updateApplyConfigVisibility() |             updateApplyConfigVisibility() | ||||||
|  |             // from clients.html | ||||||
|  |             updateSearchList() | ||||||
| 
 | 
 | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -279,6 +279,36 @@ Wireguard Clients | ||||||
|                 }); |                 }); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         function updateSearchList() { | ||||||
|  |             $.getJSON("{{.basePath}}/api/subnet-ranges", null, function(data) { | ||||||
|  |                 $("#status-selector option").remove(); | ||||||
|  |                 $("#status-selector").append( | ||||||
|  |                     $("<option></option>") | ||||||
|  |                         .text("All") | ||||||
|  |                         .val("All"), | ||||||
|  |                     $("<option></option>") | ||||||
|  |                         .text("Enabled") | ||||||
|  |                         .val("Enabled"), | ||||||
|  |                     $("<option></option>") | ||||||
|  |                         .text("Disabled") | ||||||
|  |                         .val("Disabled"), | ||||||
|  |                     $("<option></option>") | ||||||
|  |                         .text("Connected") | ||||||
|  |                         .val("Connected"), | ||||||
|  |                     $("<option></option>") | ||||||
|  |                         .text("Disconnected") | ||||||
|  |                         .val("Disconnected") | ||||||
|  |                 ); | ||||||
|  |                 $.each(data, function(index, item) { | ||||||
|  |                     $("#status-selector").append( | ||||||
|  |                         $("<option></option>") | ||||||
|  |                             .text(item) | ||||||
|  |                             .val(item) | ||||||
|  |                     ); | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  | } | ||||||
|     </script> |     </script> | ||||||
|     <script> |     <script> | ||||||
|         // load client list |         // load client list | ||||||
|  | @ -368,7 +398,18 @@ Wireguard Clients | ||||||
|                     }); |                     }); | ||||||
|                     break; |                     break; | ||||||
|                 default: |                 default: | ||||||
|                     $('.col-lg-4').show(); |                     $('.col-lg-4').hide(); | ||||||
|  |                     const selectedSR = $("#status-selector").val() | ||||||
|  |                     $(".fa-subnetrange").each(function () { | ||||||
|  |                         const srs = $(this).parent().text().trim().split(',') | ||||||
|  |                         for (const sr of srs) { | ||||||
|  |                             if (sr === selectedSR) { | ||||||
|  |                                 $(this).closest('.col-lg-4').show(); | ||||||
|  |                                 break | ||||||
|  |                             }                             | ||||||
|  |                         } | ||||||
|  |                     }) | ||||||
|  |                     // $('.col-lg-4').show(); | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | package util | ||||||
|  | 
 | ||||||
|  | var IPToSubnetRange = map[string]uint16{} | ||||||
							
								
								
									
										38
									
								
								util/util.go
								
								
								
								
							
							
						
						
									
										38
									
								
								util/util.go
								
								
								
								
							|  | @ -410,6 +410,44 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // findSubnetRangeForIP to find first SR for IP, and cache the match
 | ||||||
|  | func findSubnetRangeForIP(cidr string) (uint16, error) { | ||||||
|  | 	ip, _, err := net.ParseCIDR(cidr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if srName, ok := IPToSubnetRange[ip.String()]; ok { | ||||||
|  | 		return srName, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for srIndex, sr := range SubnetRangesOrder { | ||||||
|  | 		for _, srCIDR := range SubnetRanges[sr] { | ||||||
|  | 			if srCIDR.Contains(ip) { | ||||||
|  | 				IPToSubnetRange[ip.String()] = uint16(srIndex) | ||||||
|  | 				return uint16(srIndex), nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0, fmt.Errorf("Subnet range not foud for this IP") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FillClientSubnetRange to fill subnet ranges client belongs to, does nothing if SRs are not found
 | ||||||
|  | func FillClientSubnetRange(client model.ClientData) model.ClientData { | ||||||
|  | 	cl := *client.Client | ||||||
|  | 	for _, ip := range cl.AllocatedIPs { | ||||||
|  | 		sr, err := findSubnetRangeForIP(ip) | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		cl.SubnetRanges = append(cl.SubnetRanges, SubnetRangesOrder[sr]) | ||||||
|  | 	} | ||||||
|  | 	return model.ClientData{ | ||||||
|  | 		Client: &cl, | ||||||
|  | 		QRCode: client.QRCode, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ValidateAndFixSubnetRanges to check if subnet ranges are valid for the server configuration
 | // ValidateAndFixSubnetRanges to check if subnet ranges are valid for the server configuration
 | ||||||
| // Removes all non-valid CIDRs
 | // Removes all non-valid CIDRs
 | ||||||
| func ValidateAndFixSubnetRanges(db store.IStore) error { | func ValidateAndFixSubnetRanges(db store.IStore) error { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue