Skip to content
Merged
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
10 changes: 9 additions & 1 deletion core/host/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,15 @@ func (v *validator) validateVPNCertificate(
index int,
vpnDrivers []vpn.AvailableDriver,
) error {
fieldPath := fmt.Sprintf("vpns[%d].certificateId", index)
if hostVpn.CertificateID != nil && !hostVpn.EnableHTTPS {
v.delegate.Add(
fieldPath,
i18n.M(ctx, i18n.K.CoreHostVpnCertificateCannotBeInformedIfDisabled),
)
return nil
}

if !hostVpn.EnableHTTPS {
return nil
}
Expand All @@ -451,7 +460,6 @@ func (v *validator) validateVPNCertificate(
return nil
}

fieldPath := fmt.Sprintf("vpns[%d].certificateId", index)
if err := v.validateManagedSSLCertificate(ctx, driver, hostVpn, fieldPath); err != nil {
return err
}
Expand Down
27 changes: 27 additions & 0 deletions core/host/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,33 @@ func Test_validator(t *testing.T) {
})

t.Run("vpn certificate validation", func(t *testing.T) {
t.Run("certificate informed but https disabled", func(t *testing.T) {
hostValidator, mocks := setupValidator(t)
h := newHost()
vpnID := uuid.New()
certID := uuid.New()
h.VPNs = []VPN{
{VPNID: vpnID, Name: "vpn1", EnableHTTPS: false, CertificateID: &certID},
}

mocks.binding.EXPECT().
Validate(t.Context(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil).AnyTimes()
mocks.vpn.EXPECT().
Get(t.Context(), vpnID).
Return(&vpn.VPN{Enabled: true, Driver: "driver1"}, nil)
mocks.vpn.EXPECT().
GetAvailableDrivers(t.Context()).
Return(nil, nil).AnyTimes()

err := hostValidator.validate(t.Context(), h)
assertViolations(
t,
err,
i18n.K.CoreHostVpnCertificateCannotBeInformedIfDisabled,
)
})

t.Run("driver managed - certificate required", func(t *testing.T) {
hostValidator, mocks := setupValidator(t)
h := newHost()
Expand Down
8 changes: 7 additions & 1 deletion core/nginx/vpn_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package nginx
import (
"context"
"fmt"
"strconv"

"github.com/google/uuid"

Expand Down Expand Up @@ -224,7 +225,12 @@ func (a *endpointAdapter) Hash() string {
domainNameStr = *a.domainName
}

return a.vpnID.String() + a.name + domainNameStr
var certIDStr string
if a.certDetails != nil {
certIDStr = a.certDetails.ID.String()
}

return a.vpnID.String() + a.name + domainNameStr + strconv.FormatBool(a.enableHTTPS) + certIDStr
}

func (a *endpointAdapter) VPNID() uuid.UUID {
Expand Down
73 changes: 65 additions & 8 deletions core/nginx/vpn_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,77 @@ func Test_endpointAdapter(t *testing.T) {

t.Run("generates consistent hash", func(t *testing.T) {
adapter := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: false,
}
assert.Equal(t, id.String()+name+domain, adapter.Hash())
assert.Equal(t, id.String()+name+domain+"false", adapter.Hash())
})

t.Run("handles nil domain", func(t *testing.T) {
adapter := &endpointAdapter{
vpnID: id,
name: name,
domainName: nil,
vpnID: id,
name: name,
domainName: nil,
enableHTTPS: false,
}
assert.Equal(t, id.String()+name+"false", adapter.Hash())
})

t.Run("generates different hash when enableHTTPS changes", func(t *testing.T) {
adapterHTTPSOn := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: true,
}
adapterHTTPSOff := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: false,
}
assert.NotEqual(t, adapterHTTPSOn.Hash(), adapterHTTPSOff.Hash())
})

t.Run("generates different hash when certificate changes", func(t *testing.T) {
certID1 := uuid.New()
certID2 := uuid.New()
adapterCert1 := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: true,
certDetails: &certificate.Certificate{ID: certID1},
}
adapterCert2 := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: true,
certDetails: &certificate.Certificate{ID: certID2},
}
assert.NotEqual(t, adapterCert1.Hash(), adapterCert2.Hash())
})

t.Run("generates same hash when nothing changes", func(t *testing.T) {
certID := uuid.New()
adapter1 := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: true,
certDetails: &certificate.Certificate{ID: certID},
}
adapter2 := &endpointAdapter{
vpnID: id,
name: name,
domainName: &domain,
enableHTTPS: true,
certDetails: &certificate.Certificate{ID: certID},
}
assert.Equal(t, id.String()+name, adapter.Hash())
assert.Equal(t, adapter1.Hash(), adapter2.Hash())
})
})

Expand Down
6 changes: 6 additions & 0 deletions frontend/src/domain/host/HostConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,14 @@ class HostConverter {

private async vpnToFormValues(data: HostVpn): Promise<HostFormVpn> {
const vpn = await this.vpnService.getById(data.vpnId!!)
const certificate = this.notNull(data.certificateId)
? await this.certificateService.getById(data.certificateId!!)
: undefined

return {
...data,
vpn: vpn!!,
certificate,
}
}

Expand Down Expand Up @@ -195,6 +199,8 @@ class HostConverter {
vpnId: vpn.vpn?.id,
name: vpn.name,
host: vpn.host,
enableHttps: vpn.enableHttps ?? false,
certificateId: vpn.certificate?.id,
}

if (!vpn.host || vpn.host.trim() === "") {
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/domain/host/HostFormPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import HostRoutes from "./components/HostRoutes"
import HostBindings from "./components/HostBindings"
import "./HostFormPage.css"
import ReloadNginxAction from "../nginx/actions/ReloadNginxAction"
import HostFormValues from "./model/HostFormValues"
import HostFormValues, { HostFormVpn } from "./model/HostFormValues"
import HostConverter from "./HostConverter"
import If from "../../core/components/flowcontrol/If"
import CommonNotifications from "../../core/components/notification/CommonNotifications"
Expand Down Expand Up @@ -201,6 +201,16 @@ export default class HostFormPage extends React.Component<any, HostFormPageState
})
}

private handleVpnsChange(vpns: HostFormVpn[]) {
const { formValues } = this.state
this.setState(
{
formValues: { ...formValues, vpns },
},
() => this.formRef.current?.setFieldsValue({ vpns }),
)
}

private fetchAccessLists(
pageSize: number,
pageNumber: number,
Expand Down Expand Up @@ -424,7 +434,7 @@ export default class HostFormPage extends React.Component<any, HostFormPageState
<HostVpns
vpns={formValues.vpns}
validationResult={validationResult}
onChange={() => this.formRef.current?.setFieldsValue({ vpns: formValues.vpns })}
onChange={vpns => this.handleVpnsChange(vpns)}
/>
</Form>
)
Expand Down
30 changes: 21 additions & 9 deletions frontend/src/domain/host/components/HostVpns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface HostVpnsProps {
vpns: HostFormVpn[]
validationResult: ValidationResult
className?: string
onChange: () => void
onChange: (vpns: HostFormVpn[]) => void
}

interface HostVpnsState {
Expand All @@ -41,20 +41,32 @@ export default class HostVpns extends React.Component<HostVpnsProps, HostVpnsSta
}

private handleVpnChange(index: number, vpn?: VpnResponse) {
if (!vpn) return

const { vpns, onChange } = this.props
if (vpn) {
vpns[index].vpn = vpn
vpns[index].enableHttps = vpn.driverEndpointSslSupport === EndpointSSLSupport.PROVIDER_MANAGED
vpns[index].certificate = undefined
onChange()
const updatedVpns = [...vpns]

updatedVpns[index] = {
...vpns[index],
vpn,
enableHttps: vpn.driverEndpointSslSupport === EndpointSSLSupport.PROVIDER_MANAGED,
certificate: undefined,
}

onChange(updatedVpns)
}

private handleCertificateChange(index: number, certificate?: CertificateResponse) {
const { vpns, onChange } = this.props
vpns[index].certificate = certificate
vpns[index].enableHttps = certificate !== undefined
onChange()
const updatedVpns = [...vpns]

updatedVpns[index] = {
...vpns[index],
certificate,
enableHttps: certificate !== undefined,
}

onChange(updatedVpns)
}

private openVpnSettingsModal(index: number) {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/domain/host/model/HostRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export interface HostVpn {
vpnId: string
name: string
host?: string
enableHttps: boolean
certificateId?: string
}

export default interface HostRequest {
Expand Down
Loading
Loading