Browse Source

Merge branch 'release-v0.2.5'

Mustafa Arici 8 years ago
parent
commit
b86f7b201a

+ 14 - 0
CHANGELOG.md

@@ -1,5 +1,19 @@
 # Change Log
 
+## [v0.2.5](https://github.com/cad/ovpm/tree/v0.2.5) (2017-10-13)
+[Full Changelog](https://github.com/cad/ovpm/compare/v0.2.4...v0.2.5)
+
+**Implemented enhancements:**
+
+- WARNING: INSECURE cipher with block size less than 128 bit \(64 bit\).  [\#56](https://github.com/cad/ovpm/issues/56)
+- expose vpn restart functionality over api [\#50](https://github.com/cad/ovpm/issues/50)
+
+**Fixed bugs:**
+
+- WEBUI \> NETWORKS [\#55](https://github.com/cad/ovpm/issues/55)
+- block giving vpn server's internal ip to some user as static ip [\#54](https://github.com/cad/ovpm/issues/54)
+- when starting ensure emit [\#51](https://github.com/cad/ovpm/issues/51)
+
 ## [v0.2.4](https://github.com/cad/ovpm/tree/v0.2.4) (2017-10-06)
 [Full Changelog](https://github.com/cad/ovpm/compare/v0.2.3...v0.2.4)
 

+ 1 - 1
README.md

@@ -59,7 +59,7 @@ $ systemctl enable ovpmd
 
 **from Source (go get):**
 
-Only dependency for ovpm is **OpenVPN>=2.3**.
+Only dependency for ovpm is **OpenVPN>=2.3.3**.
 
 ```bash
 $ go get -u github.com/cad/ovpm/...

+ 9 - 1
api/pb/auth.pb.gw.go

@@ -78,7 +78,15 @@ func RegisterAuthServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.Se
 // RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewAuthServiceClient(conn)
+	return RegisterAuthServiceHandlerClient(ctx, mux, NewAuthServiceClient(conn))
+}
+
+// RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "AuthServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "AuthServiceClient" to call the correct interceptors.
+func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthServiceClient) error {
 
 	mux.Handle("GET", pattern_AuthService_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)

+ 9 - 1
api/pb/network.pb.gw.go

@@ -143,7 +143,15 @@ func RegisterNetworkServiceHandlerFromEndpoint(ctx context.Context, mux *runtime
 // RegisterNetworkServiceHandler registers the http handlers for service NetworkService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterNetworkServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewNetworkServiceClient(conn)
+	return RegisterNetworkServiceHandlerClient(ctx, mux, NewNetworkServiceClient(conn))
+}
+
+// RegisterNetworkServiceHandler registers the http handlers for service NetworkService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "NetworkServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "NetworkServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "NetworkServiceClient" to call the correct interceptors.
+func RegisterNetworkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client NetworkServiceClient) error {
 
 	mux.Handle("POST", pattern_NetworkService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)

+ 2 - 0
api/pb/user.pb.go

@@ -22,9 +22,11 @@ It has these top-level messages:
 	VPNStatusRequest
 	VPNInitRequest
 	VPNUpdateRequest
+	VPNRestartRequest
 	VPNStatusResponse
 	VPNInitResponse
 	VPNUpdateResponse
+	VPNRestartResponse
 	NetworkCreateRequest
 	NetworkListRequest
 	NetworkDeleteRequest

+ 9 - 1
api/pb/user.pb.gw.go

@@ -130,7 +130,15 @@ func RegisterUserServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.Se
 // RegisterUserServiceHandler registers the http handlers for service UserService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewUserServiceClient(conn)
+	return RegisterUserServiceHandlerClient(ctx, mux, NewUserServiceClient(conn))
+}
+
+// RegisterUserServiceHandler registers the http handlers for service UserService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "UserServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "UserServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "UserServiceClient" to call the correct interceptors.
+func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UserServiceClient) error {
 
 	mux.Handle("GET", pattern_UserService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)

+ 88 - 35
api/pb/vpn.pb.go

@@ -122,6 +122,14 @@ func (m *VPNUpdateRequest) GetDns() string {
 	return ""
 }
 
+type VPNRestartRequest struct {
+}
+
+func (m *VPNRestartRequest) Reset()                    { *m = VPNRestartRequest{} }
+func (m *VPNRestartRequest) String() string            { return proto.CompactTextString(m) }
+func (*VPNRestartRequest) ProtoMessage()               {}
+func (*VPNRestartRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
+
 type VPNStatusResponse struct {
 	Name         string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
 	SerialNumber string `protobuf:"bytes,2,opt,name=serial_number,json=serialNumber" json:"serial_number,omitempty"`
@@ -139,7 +147,7 @@ type VPNStatusResponse struct {
 func (m *VPNStatusResponse) Reset()                    { *m = VPNStatusResponse{} }
 func (m *VPNStatusResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNStatusResponse) ProtoMessage()               {}
-func (*VPNStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
+func (*VPNStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
 
 func (m *VPNStatusResponse) GetName() string {
 	if m != nil {
@@ -224,7 +232,7 @@ type VPNInitResponse struct {
 func (m *VPNInitResponse) Reset()                    { *m = VPNInitResponse{} }
 func (m *VPNInitResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNInitResponse) ProtoMessage()               {}
-func (*VPNInitResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
+func (*VPNInitResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} }
 
 type VPNUpdateResponse struct {
 }
@@ -232,15 +240,25 @@ type VPNUpdateResponse struct {
 func (m *VPNUpdateResponse) Reset()                    { *m = VPNUpdateResponse{} }
 func (m *VPNUpdateResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNUpdateResponse) ProtoMessage()               {}
-func (*VPNUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} }
+func (*VPNUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} }
+
+type VPNRestartResponse struct {
+}
+
+func (m *VPNRestartResponse) Reset()                    { *m = VPNRestartResponse{} }
+func (m *VPNRestartResponse) String() string            { return proto.CompactTextString(m) }
+func (*VPNRestartResponse) ProtoMessage()               {}
+func (*VPNRestartResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} }
 
 func init() {
 	proto.RegisterType((*VPNStatusRequest)(nil), "pb.VPNStatusRequest")
 	proto.RegisterType((*VPNInitRequest)(nil), "pb.VPNInitRequest")
 	proto.RegisterType((*VPNUpdateRequest)(nil), "pb.VPNUpdateRequest")
+	proto.RegisterType((*VPNRestartRequest)(nil), "pb.VPNRestartRequest")
 	proto.RegisterType((*VPNStatusResponse)(nil), "pb.VPNStatusResponse")
 	proto.RegisterType((*VPNInitResponse)(nil), "pb.VPNInitResponse")
 	proto.RegisterType((*VPNUpdateResponse)(nil), "pb.VPNUpdateResponse")
+	proto.RegisterType((*VPNRestartResponse)(nil), "pb.VPNRestartResponse")
 	proto.RegisterEnum("pb.VPNProto", VPNProto_name, VPNProto_value)
 }
 
@@ -258,6 +276,7 @@ type VPNServiceClient interface {
 	Status(ctx context.Context, in *VPNStatusRequest, opts ...grpc.CallOption) (*VPNStatusResponse, error)
 	Init(ctx context.Context, in *VPNInitRequest, opts ...grpc.CallOption) (*VPNInitResponse, error)
 	Update(ctx context.Context, in *VPNUpdateRequest, opts ...grpc.CallOption) (*VPNUpdateResponse, error)
+	Restart(ctx context.Context, in *VPNRestartRequest, opts ...grpc.CallOption) (*VPNRestartResponse, error)
 }
 
 type vPNServiceClient struct {
@@ -295,12 +314,22 @@ func (c *vPNServiceClient) Update(ctx context.Context, in *VPNUpdateRequest, opt
 	return out, nil
 }
 
+func (c *vPNServiceClient) Restart(ctx context.Context, in *VPNRestartRequest, opts ...grpc.CallOption) (*VPNRestartResponse, error) {
+	out := new(VPNRestartResponse)
+	err := grpc.Invoke(ctx, "/pb.VPNService/Restart", in, out, c.cc, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // Server API for VPNService service
 
 type VPNServiceServer interface {
 	Status(context.Context, *VPNStatusRequest) (*VPNStatusResponse, error)
 	Init(context.Context, *VPNInitRequest) (*VPNInitResponse, error)
 	Update(context.Context, *VPNUpdateRequest) (*VPNUpdateResponse, error)
+	Restart(context.Context, *VPNRestartRequest) (*VPNRestartResponse, error)
 }
 
 func RegisterVPNServiceServer(s *grpc.Server, srv VPNServiceServer) {
@@ -361,6 +390,24 @@ func _VPNService_Update_Handler(srv interface{}, ctx context.Context, dec func(i
 	return interceptor(ctx, in, info, handler)
 }
 
+func _VPNService_Restart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(VPNRestartRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(VPNServiceServer).Restart(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.VPNService/Restart",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(VPNServiceServer).Restart(ctx, req.(*VPNRestartRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _VPNService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "pb.VPNService",
 	HandlerType: (*VPNServiceServer)(nil),
@@ -377,6 +424,10 @@ var _VPNService_serviceDesc = grpc.ServiceDesc{
 			MethodName: "Update",
 			Handler:    _VPNService_Update_Handler,
 		},
+		{
+			MethodName: "Restart",
+			Handler:    _VPNService_Restart_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "vpn.proto",
@@ -385,37 +436,39 @@ var _VPNService_serviceDesc = grpc.ServiceDesc{
 func init() { proto.RegisterFile("vpn.proto", fileDescriptor1) }
 
 var fileDescriptor1 = []byte{
-	// 502 bytes of a gzipped FileDescriptorProto
+	// 537 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6e, 0xd3, 0x40,
-	0x10, 0x86, 0xb1, 0x93, 0x3a, 0xc9, 0x50, 0x8a, 0x3b, 0x4d, 0x55, 0x13, 0xa8, 0x54, 0x99, 0x4b,
-	0x14, 0xa4, 0x44, 0x94, 0x5b, 0x2f, 0x08, 0x0a, 0x48, 0x48, 0xc8, 0x98, 0xd0, 0xe6, 0x6a, 0xad,
-	0x9d, 0x6d, 0xb1, 0x9a, 0xec, 0x2e, 0xde, 0x4d, 0x1e, 0x00, 0x1e, 0x01, 0x89, 0x03, 0xaf, 0xc5,
-	0x2b, 0xf0, 0x20, 0xc8, 0xb3, 0x4e, 0x6a, 0x23, 0x7a, 0x9b, 0xfd, 0x67, 0xf7, 0xd7, 0x3f, 0xdf,
-	0x0e, 0xf4, 0xd6, 0x4a, 0x8c, 0x55, 0x21, 0x8d, 0x44, 0x57, 0xa5, 0x83, 0x27, 0xd7, 0x52, 0x5e,
-	0x2f, 0xf8, 0x84, 0xa9, 0x7c, 0xc2, 0x84, 0x90, 0x86, 0x99, 0x5c, 0x0a, 0x6d, 0x6f, 0x84, 0x08,
-	0xfe, 0x2c, 0x8e, 0x3e, 0x1b, 0x66, 0x56, 0x7a, 0xca, 0xbf, 0xae, 0xb8, 0x36, 0xe1, 0x2f, 0x07,
-	0xf6, 0x66, 0x71, 0xf4, 0x5e, 0xe4, 0xa6, 0x92, 0x70, 0x00, 0xdd, 0x2f, 0x52, 0x1b, 0xc1, 0x96,
-	0x3c, 0x70, 0x4e, 0x9c, 0x61, 0x6f, 0xba, 0x3d, 0x23, 0x42, 0x5b, 0xc9, 0xc2, 0x04, 0x2e, 0xe9,
-	0x54, 0xe3, 0x33, 0x00, 0xf2, 0x4f, 0x54, 0xc1, 0xaf, 0x82, 0xd6, 0x89, 0x33, 0xdc, 0x3b, 0xdd,
-	0x1d, 0xab, 0x74, 0x3c, 0x8b, 0xa3, 0xb8, 0x6c, 0x4c, 0x7b, 0xd4, 0x8f, 0x0b, 0x7e, 0x85, 0x8f,
-	0xa0, 0x9b, 0xab, 0x24, 0x5d, 0xc8, 0xec, 0x26, 0x68, 0x93, 0x49, 0x27, 0x57, 0xaf, 0xcb, 0x23,
-	0xfa, 0xd0, 0x9a, 0x0b, 0x1d, 0xec, 0x90, 0x5a, 0x96, 0xe1, 0x4b, 0x0a, 0x7c, 0xa9, 0xe6, 0xcc,
-	0xf0, 0x4d, 0xba, 0xba, 0x81, 0xf3, 0x5f, 0x03, 0xf7, 0xd6, 0xe0, 0xa7, 0x0b, 0xfb, 0xb5, 0x91,
-	0xb5, 0x92, 0x42, 0xd3, 0x10, 0xb5, 0xe1, 0xa8, 0xc6, 0xa7, 0xf0, 0x40, 0xf3, 0x22, 0x67, 0x8b,
-	0x44, 0xac, 0x96, 0x29, 0x2f, 0x2a, 0x97, 0x5d, 0x2b, 0x46, 0xa4, 0x35, 0xc8, 0xb4, 0xee, 0x20,
-	0xd3, 0xae, 0x91, 0x41, 0x68, 0x67, 0xbc, 0x30, 0xd5, 0x48, 0x54, 0xe3, 0x11, 0x74, 0x32, 0x96,
-	0x90, 0xec, 0x91, 0xec, 0x65, 0xec, 0xbc, 0x6c, 0xf8, 0xd0, 0x12, 0xdc, 0x04, 0x1d, 0x9b, 0x5e,
-	0x70, 0x7a, 0xbe, 0x64, 0xfa, 0x26, 0xe8, 0xda, 0xe7, 0x65, 0x8d, 0xc7, 0x00, 0x59, 0xc1, 0x99,
-	0xe1, 0xf3, 0x84, 0x99, 0xa0, 0x47, 0x9d, 0x5e, 0xa5, 0xbc, 0x32, 0xd8, 0x87, 0x1d, 0x62, 0x1d,
-	0x00, 0x75, 0xec, 0x61, 0x03, 0xe6, 0xfe, 0x2d, 0x98, 0x7d, 0x78, 0xb8, 0xfd, 0x75, 0x4b, 0x25,
-	0x3c, 0x20, 0x54, 0x1b, 0xd8, 0x56, 0x1c, 0x0d, 0xa1, 0xbb, 0xf9, 0x45, 0x04, 0xf0, 0xa2, 0x8f,
-	0xf1, 0xf4, 0xed, 0x3b, 0xff, 0x1e, 0x76, 0xa0, 0x75, 0xf9, 0x26, 0xf6, 0x9d, 0xb2, 0xb8, 0x38,
-	0x8f, 0x7d, 0xf7, 0xf4, 0xbb, 0x0b, 0x50, 0xa2, 0xe6, 0xc5, 0x3a, 0xcf, 0x38, 0x7e, 0x02, 0xcf,
-	0x52, 0xc7, 0x7e, 0xb5, 0x0a, 0x8d, 0xbd, 0x1b, 0x1c, 0xfe, 0xa3, 0x56, 0x21, 0x06, 0xdf, 0x7e,
-	0xff, 0xf9, 0xe1, 0xf6, 0x11, 0x69, 0x85, 0xd7, 0xcf, 0x27, 0x6b, 0x25, 0x26, 0xda, 0x1a, 0x7d,
-	0x80, 0x76, 0x19, 0x18, 0xb1, 0x7a, 0x5a, 0xdb, 0xd9, 0xc1, 0x41, 0x43, 0xab, 0xcc, 0x1e, 0x93,
-	0xd9, 0x61, 0xe8, 0xd7, 0xcd, 0x72, 0x91, 0x9b, 0x33, 0x67, 0x84, 0x17, 0xe0, 0xd9, 0x59, 0xb7,
-	0x01, 0x1b, 0x7b, 0xb6, 0x0d, 0xd8, 0x04, 0x12, 0x1e, 0x93, 0xe7, 0x51, 0xd8, 0x08, 0xb8, 0xa2,
-	0x3b, 0x67, 0xce, 0x28, 0xf5, 0x08, 0xf8, 0x8b, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x57,
-	0x61, 0xfa, 0x98, 0x03, 0x00, 0x00,
+	0x10, 0x86, 0xb1, 0x93, 0x3a, 0xc9, 0x50, 0x8a, 0x3b, 0x4d, 0xa9, 0x31, 0x54, 0xaa, 0xcc, 0xa5,
+	0x2a, 0x52, 0x22, 0xca, 0x8d, 0x0b, 0x82, 0x02, 0x12, 0x12, 0x32, 0x26, 0x34, 0xb9, 0x46, 0x1b,
+	0x67, 0x5b, 0xac, 0x26, 0xeb, 0xc5, 0xbb, 0xc9, 0x03, 0xf0, 0x0a, 0x48, 0x1c, 0x78, 0x2d, 0x6e,
+	0x9c, 0x79, 0x10, 0xe4, 0xd9, 0x75, 0x6a, 0x47, 0x70, 0x9b, 0xfd, 0x67, 0xe7, 0xd3, 0xcc, 0x3f,
+	0x03, 0xbd, 0xb5, 0x14, 0x03, 0x59, 0xe4, 0x3a, 0x47, 0x57, 0xce, 0xc2, 0xc7, 0xd7, 0x79, 0x7e,
+	0xbd, 0xe0, 0x43, 0x26, 0xb3, 0x21, 0x13, 0x22, 0xd7, 0x4c, 0x67, 0xb9, 0x50, 0xe6, 0x47, 0x84,
+	0xe0, 0x4f, 0x92, 0xf8, 0xb3, 0x66, 0x7a, 0xa5, 0x46, 0xfc, 0xeb, 0x8a, 0x2b, 0x1d, 0xfd, 0x74,
+	0x60, 0x6f, 0x92, 0xc4, 0xef, 0x45, 0xa6, 0xad, 0x84, 0x21, 0x74, 0xbf, 0xe4, 0x4a, 0x0b, 0xb6,
+	0xe4, 0x81, 0x73, 0xe2, 0x9c, 0xf6, 0x46, 0x9b, 0x37, 0x22, 0xb4, 0x65, 0x5e, 0xe8, 0xc0, 0x25,
+	0x9d, 0x62, 0x7c, 0x0a, 0x40, 0xfc, 0xa9, 0x2c, 0xf8, 0x55, 0xd0, 0x3a, 0x71, 0x4e, 0xf7, 0xce,
+	0x77, 0x07, 0x72, 0x36, 0x98, 0x24, 0x71, 0x52, 0x26, 0x46, 0x3d, 0xca, 0x27, 0x05, 0xbf, 0xc2,
+	0x87, 0xd0, 0xcd, 0xe4, 0x74, 0xb6, 0xc8, 0xd3, 0x9b, 0xa0, 0x4d, 0x90, 0x4e, 0x26, 0x5f, 0x97,
+	0x4f, 0xf4, 0xa1, 0x35, 0x17, 0x2a, 0xd8, 0x21, 0xb5, 0x0c, 0xa3, 0x97, 0xd4, 0xf0, 0x58, 0xce,
+	0x99, 0xe6, 0x55, 0x77, 0x75, 0x80, 0xf3, 0x4f, 0x80, 0x7b, 0x0b, 0x38, 0x80, 0xfd, 0x49, 0x12,
+	0x8f, 0xb8, 0xd2, 0xac, 0xa8, 0xe6, 0x8b, 0x7e, 0xb8, 0xa4, 0x56, 0x3e, 0x28, 0x99, 0x0b, 0x45,
+	0x93, 0xd5, 0x26, 0xa6, 0x18, 0x9f, 0xc0, 0x3d, 0xc5, 0x8b, 0x8c, 0x2d, 0xa6, 0x62, 0xb5, 0x9c,
+	0xf1, 0xc2, 0xa2, 0x77, 0x8d, 0x18, 0x93, 0xd6, 0xb0, 0xab, 0xf5, 0x1f, 0xbb, 0xda, 0x35, 0xbb,
+	0x10, 0xda, 0x29, 0x2f, 0xb4, 0x9d, 0x93, 0x62, 0x3c, 0x82, 0x4e, 0xca, 0xa6, 0x24, 0x7b, 0x24,
+	0x7b, 0x29, 0xbb, 0x28, 0x13, 0x3e, 0xb4, 0x04, 0xd7, 0x41, 0xc7, 0x8c, 0x24, 0x38, 0x95, 0x2f,
+	0x99, 0xba, 0x09, 0xba, 0xa6, 0xbc, 0x8c, 0xf1, 0x18, 0x20, 0x2d, 0x38, 0xd3, 0x7c, 0x3e, 0x65,
+	0x3a, 0xe8, 0x51, 0xa6, 0x67, 0x95, 0x57, 0x1a, 0xfb, 0xb0, 0x43, 0x0b, 0x08, 0x80, 0x32, 0xe6,
+	0x51, 0xb9, 0x75, 0xf7, 0xd6, 0xad, 0x7d, 0xb8, 0xbf, 0x39, 0x05, 0xe3, 0x8a, 0x35, 0xb0, 0xda,
+	0x80, 0x15, 0xfb, 0x80, 0x75, 0x57, 0x8d, 0x7a, 0x76, 0x0a, 0xdd, 0x6a, 0xe1, 0x08, 0xe0, 0xc5,
+	0x1f, 0x93, 0xd1, 0xdb, 0x77, 0xfe, 0x1d, 0xec, 0x40, 0x6b, 0xfc, 0x26, 0xf1, 0x9d, 0x32, 0xb8,
+	0xbc, 0x48, 0x7c, 0xf7, 0xfc, 0xb7, 0x0b, 0x50, 0x2e, 0x80, 0x17, 0xeb, 0x2c, 0xe5, 0xf8, 0x09,
+	0x3c, 0xb3, 0x0b, 0xec, 0xdb, 0xab, 0x69, 0x9c, 0x68, 0x78, 0xb8, 0xa5, 0xda, 0x2e, 0xc2, 0x6f,
+	0xbf, 0xfe, 0x7c, 0x77, 0xfb, 0x88, 0x74, 0xed, 0xeb, 0x67, 0xc3, 0xb5, 0x14, 0x43, 0x65, 0x40,
+	0x1f, 0xa0, 0x5d, 0x8e, 0x81, 0x68, 0x4b, 0x6b, 0xe7, 0x1d, 0x1e, 0x34, 0x34, 0x0b, 0x7b, 0x44,
+	0xb0, 0xc3, 0xc8, 0xaf, 0xc3, 0x32, 0x91, 0xe9, 0x17, 0xce, 0x19, 0x5e, 0x82, 0x67, 0x1c, 0xd8,
+	0x34, 0xd8, 0x38, 0xc9, 0x4d, 0x83, 0x5b, 0x36, 0x1d, 0x13, 0xf3, 0x28, 0x6a, 0x34, 0xb8, 0xa2,
+	0x3f, 0x25, 0x75, 0x0c, 0x1d, 0x6b, 0x21, 0x56, 0x80, 0xe6, 0xa1, 0x86, 0x0f, 0xb6, 0xe5, 0xad,
+	0x66, 0x0f, 0xea, 0xe0, 0xc2, 0x7c, 0x9a, 0x79, 0xb4, 0xdd, 0xe7, 0x7f, 0x03, 0x00, 0x00, 0xff,
+	0xff, 0x04, 0xc8, 0xb8, 0x7c, 0x1a, 0x04, 0x00, 0x00,
 }

+ 51 - 1
api/pb/vpn.pb.gw.go

@@ -63,6 +63,15 @@ func request_VPNService_Update_0(ctx context.Context, marshaler runtime.Marshale
 
 }
 
+func request_VPNService_Restart_0(ctx context.Context, marshaler runtime.Marshaler, client VPNServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq VPNRestartRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := client.Restart(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
 // RegisterVPNServiceHandlerFromEndpoint is same as RegisterVPNServiceHandler but
 // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
 func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@@ -91,7 +100,15 @@ func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.Ser
 // RegisterVPNServiceHandler registers the http handlers for service VPNService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterVPNServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewVPNServiceClient(conn)
+	return RegisterVPNServiceHandlerClient(ctx, mux, NewVPNServiceClient(conn))
+}
+
+// RegisterVPNServiceHandler registers the http handlers for service VPNService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "VPNServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "VPNServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "VPNServiceClient" to call the correct interceptors.
+func RegisterVPNServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client VPNServiceClient) error {
 
 	mux.Handle("GET", pattern_VPNService_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)
@@ -180,6 +197,35 @@ func RegisterVPNServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn
 
 	})
 
+	mux.Handle("POST", pattern_VPNService_Restart_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(ctx)
+		defer cancel()
+		if cn, ok := w.(http.CloseNotifier); ok {
+			go func(done <-chan struct{}, closed <-chan bool) {
+				select {
+				case <-done:
+				case <-closed:
+					cancel()
+				}
+			}(ctx.Done(), cn.CloseNotify())
+		}
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_VPNService_Restart_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_VPNService_Restart_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -189,6 +235,8 @@ var (
 	pattern_VPNService_Init_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "init"}, ""))
 
 	pattern_VPNService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "update"}, ""))
+
+	pattern_VPNService_Restart_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "restart"}, ""))
 )
 
 var (
@@ -197,4 +245,6 @@ var (
 	forward_VPNService_Init_0 = runtime.ForwardResponseMessage
 
 	forward_VPNService_Update_0 = runtime.ForwardResponseMessage
+
+	forward_VPNService_Restart_0 = runtime.ForwardResponseMessage
 )

+ 8 - 0
api/pb/vpn.proto

@@ -23,6 +23,7 @@ message VPNUpdateRequest {
   string ip_block = 1;
   string dns = 2;
 }
+message VPNRestartRequest {}
 
 
 service VPNService {
@@ -41,6 +42,12 @@ service VPNService {
       post: "/api/v1/vpn/update"
       body: "*"
     };}
+  rpc Restart (VPNRestartRequest) returns (VPNRestartResponse) {
+    option (google.api.http) = {
+      post: "/api/v1/vpn/restart"
+      //body: "*"
+    };}
+
 
 }
 
@@ -59,3 +66,4 @@ message VPNStatusResponse {
 }
 message VPNInitResponse {}
 message VPNUpdateResponse {}
+message VPNRestartResponse {}

+ 15 - 0
api/rpc.go

@@ -392,6 +392,21 @@ func (s *VPNService) Update(ctx context.Context, req *pb.VPNUpdateRequest) (*pb.
 	return &pb.VPNUpdateResponse{}, nil
 }
 
+func (s *VPNService) Restart(ctx context.Context, req *pb.VPNRestartRequest) (*pb.VPNRestartResponse, error) {
+	logrus.Debugf("rpc call: vpn restart")
+	perms, err := permset.FromContext(ctx)
+	if err != nil {
+		return nil, grpc.Errorf(codes.Unauthenticated, "Can't get permset from context")
+	}
+
+	if !perms.Contains(ovpm.RestartVPNPerm) {
+		return nil, grpc.Errorf(codes.PermissionDenied, "ovpm.UpdateVPNPerm is required for this operation.")
+	}
+
+	ovpm.RestartVPNProc()
+	return &pb.VPNRestartResponse{}, nil
+}
+
 type NetworkService struct{}
 
 func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (*pb.NetworkListResponse, error) {

File diff suppressed because it is too large
+ 1 - 1
bindata/bindata.go


+ 1 - 0
cmd/ovpm/main.go

@@ -55,6 +55,7 @@ func main() {
 				vpnStatusCommand,
 				vpnInitCommand,
 				vpnUpdateCommand,
+				vpnRestartCommand,
 			},
 		},
 		{

+ 20 - 0
cmd/ovpm/vpn.go

@@ -220,3 +220,23 @@ var vpnUpdateCommand = cli.Command{
 		return nil
 	},
 }
+
+var vpnRestartCommand = cli.Command{
+	Name:    "restart",
+	Usage:   "Restart VPN server.",
+	Aliases: []string{"s"},
+	Action: func(c *cli.Context) error {
+		conn := getConn(c.GlobalString("daemon-port"))
+		defer conn.Close()
+		vpnSvc := pb.NewVPNServiceClient(conn)
+
+		_, err := vpnSvc.Restart(context.Background(), &pb.VPNRestartRequest{})
+		if err != nil {
+			os.Exit(1)
+			return err
+		}
+
+		logrus.Info("ovpm server restarted")
+		return nil
+	},
+}

+ 2 - 2
const.go

@@ -2,7 +2,7 @@ package ovpm
 
 const (
 	// Version defines the version of ovpm.
-	Version = "0.2.4"
+	Version = "0.2.5"
 
 	// DefaultVPNPort is the default OpenVPN port to listen.
 	DefaultVPNPort = "1197"
@@ -22,7 +22,7 @@ const (
 	_DefaultConfigPath   = etcBasePath + "ovpm.ini"
 	_DefaultDBPath       = varBasePath + "db.sqlite3"
 	_DefaultVPNConfPath  = varBasePath + "server.conf"
-	_DefaultVPNCCDPath   = varBasePath + "ccd/"
+	_DefaultVPNCCDPath   = varBasePath + "ccd"
 	_DefaultCertPath     = varBasePath + "server.crt"
 	_DefaultKeyPath      = varBasePath + "server.key"
 	_DefaultCACertPath   = varBasePath + "ca.crt"

+ 1 - 1
contrib/afterupgrade.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
+systemctl daemon-reload
 if [ "`systemctl is-active ovpmd`" != "active" ]
 then
-    systemctl daemon-reload
     systemctl restart ovpmd
 fi

+ 1 - 1
contrib/systemd/ovpmd.service

@@ -12,7 +12,7 @@ Conflicts=shutdown.target
 [Service]
 TimeoutSec=5min
 PIDFile=/var/run/ovpmd.pid
-ExecStart=/usr/sbin/ovpmd --verbose
+ExecStart=/usr/sbin/ovpmd
 
 [Install]
 WantedBy=multi-user.target

+ 8 - 11
net.go

@@ -100,9 +100,6 @@ func GetNetwork(name string) (*Network, error) {
 	if govalidator.IsNull(name) {
 		return nil, fmt.Errorf("validation error: %s can not be null", name)
 	}
-	if !govalidator.IsAlphanumeric(name) {
-		return nil, fmt.Errorf("validation error: `%s` can only contain letters and numbers", name)
-	}
 
 	var network dbNetworkModel
 	db.Preload("Users").Where(&dbNetworkModel{Name: name}).First(&network)
@@ -134,8 +131,8 @@ func CreateNewNetwork(name, cidr string, nettype NetworkType, via string) (*Netw
 	if govalidator.IsNull(name) {
 		return nil, fmt.Errorf("validation error: %s can not be null", name)
 	}
-	if !govalidator.Matches(name, "[\\w.]+") { // allow alphanumeric + dot
-		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers and dots", name)
+	if !govalidator.Matches(name, "^([\\w\\.]+)$") { // allow alphanumeric, underscore and dot
+		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers, underscores and dots", name)
 	}
 	if !govalidator.IsCIDR(cidr) {
 		return nil, fmt.Errorf("validation error: `%s` must be a network in the CIDR form", cidr)
@@ -178,7 +175,7 @@ func CreateNewNetwork(name, cidr string, nettype NetworkType, via string) (*Netw
 	if db.NewRecord(&network) {
 		return nil, fmt.Errorf("can not create network in the db")
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("network defined: %s (%s)", network.Name, network.CIDR)
 	return &Network{dbNetworkModel: network}, nil
 
@@ -191,7 +188,7 @@ func (n *Network) Delete() error {
 	}
 
 	db.Unscoped().Delete(n.dbNetworkModel)
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("network deleted: %s", n.Name)
 	return nil
 }
@@ -224,7 +221,7 @@ func (n *Network) Associate(username string) error {
 	if userAssoc.Error != nil {
 		return fmt.Errorf("association failed: %v", userAssoc.Error)
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("user '%s' is associated with the network '%s'", user.GetUsername(), n.Name)
 	return nil
 }
@@ -258,7 +255,7 @@ func (n *Network) Dissociate(username string) error {
 	if userAssoc.Error != nil {
 		return fmt.Errorf("disassociation failed: %v", userAssoc.Error)
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("user '%s' is dissociated with the network '%s'", user.GetUsername(), n.Name)
 	return nil
 }
@@ -516,12 +513,12 @@ func enableNat() error {
 func HostID2IP(hostid uint32) net.IP {
 	ip := make([]byte, 4)
 	binary.BigEndian.PutUint32(ip, hostid)
-	return net.IP(ip)
+	return net.IP(ip).To4()
 }
 
 // IP2HostID converts an IP address to a host id (32-bit unsigned integer).
 func IP2HostID(ip net.IP) uint32 {
-	hostid := binary.BigEndian.Uint32(ip)
+	hostid := binary.BigEndian.Uint32(ip.To4())
 	return hostid
 }
 

+ 22 - 0
net_test.go

@@ -1,6 +1,7 @@
 package ovpm
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -49,6 +50,27 @@ func TestVPNCreateNewNetwork(t *testing.T) {
 		t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", netType, network.Type)
 	}
 
+	// Test username validation.
+	var networknametests = []struct {
+		networkname string
+		ok          bool
+	}{
+		{"asdf1240asfd", true},
+		{"asdf.asfd", true},
+		{"asdf12.12asfd", true},
+		{"asd1f-as4fd", false},
+		{"as0df a01sfd", false},
+		{"as0df$a01sfd", false},
+		{"as0df#a01sfd", false},
+		{"a6sdf_as1fd", true},
+	}
+
+	for i, tt := range networknametests {
+		_, err := CreateNewNetwork(tt.networkname, fmt.Sprintf("192.168.%d.0/24", i), SERVERNET, "")
+		if ok := (err == nil); ok != tt.ok {
+			t.Fatalf("expcted condition failed '%s': %v", tt.networkname, err)
+		}
+	}
 }
 
 func TestVPNDeleteNetwork(t *testing.T) {

+ 2 - 0
perms.go

@@ -19,6 +19,7 @@ const (
 	GetVPNStatusPerm
 	InitVPNPerm
 	UpdateVPNPerm
+	RestartVPNPerm
 
 	// Network permissions
 	ListNetworksPerm
@@ -45,6 +46,7 @@ func AdminPerms() []permset.Perm {
 		GetVPNStatusPerm,
 		InitVPNPerm,
 		UpdateVPNPerm,
+		RestartVPNPerm,
 		ListNetworksPerm,
 		CreateNetworkPerm,
 		DeleteNetworkPerm,

+ 5 - 1
supervisor/supervisor.go

@@ -252,8 +252,12 @@ func (p *Process) run(state State) func() {
 			}
 
 			// Process started successfully.
+			if p.cmd.Process == nil {
+				logrus.Debugf("p.cmd.Process was not created")
+				p.transitionTo(FAILED)
+				return
+			}
 			logrus.Debugf("process is started %s PID %d", p.executable, p.cmd.Process.Pid)
-
 			// Process Observer
 			go func() {
 				err := p.cmd.Wait()

File diff suppressed because it is too large
+ 0 - 0
template/bundle.js


+ 2 - 0
template/client.ovpn.tmpl

@@ -7,7 +7,9 @@ proto {{ .Proto }}
 remote {{ .Hostname }} {{ .Port }}
 resolv-retry infinite
 ns-cert-type server
+cipher AES-128-CBC
 nobind
+keepalive 2 20
 persist-key
 persist-tun
 comp-lzo

+ 2 - 1
template/server.conf.tmpl

@@ -212,7 +212,7 @@ client-to-client
 # Ping every 10 seconds, assume that remote
 # peer is down if no ping received during
 # a 120 second time period.
-keepalive 10 120
+keepalive 2 20
 
 # For extra security beyond that provided
 # by SSL/TLS, create an "HMAC firewall"
@@ -233,6 +233,7 @@ keepalive 10 120
 ;cipher BF-CBC        # Blowfish (default)
 ;cipher AES-128-CBC   # AES
 ;cipher DES-EDE3-CBC  # Triple-DES
+cipher AES-128-CBC
 
 # Enable compression on the VPN link.
 # If you enable it here, you must also

+ 19 - 0
template/vpn.swagger.json

@@ -41,6 +41,22 @@
         ]
       }
     },
+    "/api/v1/vpn/restart": {
+      "post": {
+        "operationId": "Restart",
+        "responses": {
+          "200": {
+            "description": "",
+            "schema": {
+              "$ref": "#/definitions/pbVPNRestartResponse"
+            }
+          }
+        },
+        "tags": [
+          "VPNService"
+        ]
+      }
+    },
     "/api/v1/vpn/status": {
       "get": {
         "operationId": "Status",
@@ -117,6 +133,9 @@
       ],
       "default": "NOPREF"
     },
+    "pbVPNRestartResponse": {
+      "type": "object"
+    },
     "pbVPNStatusResponse": {
       "type": "object",
       "properties": {

+ 22 - 8
user.go

@@ -132,8 +132,8 @@ func CreateNewUser(username, password string, nogw bool, hostid uint32, admin bo
 	if govalidator.IsNull(username) {
 		return nil, fmt.Errorf("validation error: %s can not be null", username)
 	}
-	if !govalidator.Matches(username, "[\\w.]+") { // allow alphanumeric + dot
-		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers and dots", username)
+	if !govalidator.Matches(username, "^([\\w\\.]+)$") { // allow alphanumeric, underscore and dot
+		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers, underscores and dots", username)
 	}
 	if username == "root" {
 		return nil, fmt.Errorf("forbidden: username root is reserved and can not be used")
@@ -167,6 +167,20 @@ func CreateNewUser(username, password string, nogw bool, hostid uint32, admin bo
 		if hostIDsContains(getStaticHostIDs(), hostid) {
 			return nil, fmt.Errorf("ip %s is already allocated", ip)
 		}
+
+		// Check if requested ip is allocated to the VPN server itself.
+		serverNet := net.IPNet{
+			IP:   net.ParseIP(server.Net).To4(),
+			Mask: net.IPMask(net.ParseIP(server.Mask).To4()),
+		}
+
+		ip, ipnet, err := net.ParseCIDR(serverNet.String())
+		if err != nil {
+			return nil, fmt.Errorf("can not parse: %v", err)
+		}
+		if hostid == IP2HostID(ipnet.IP)+1 { // If it's VPN server's IP addr, then don't allow it.
+			return nil, fmt.Errorf("can't assign server's ip address to a user")
+		}
 	}
 	user := dbUserModel{
 		Username:           username,
@@ -186,8 +200,8 @@ func CreateNewUser(username, password string, nogw bool, hostid uint32, admin bo
 	}
 	logrus.Infof("user created: %s", username)
 
-	// Emit server config
-	err = Emit()
+	// EmitWithRestart server config
+	err = EmitWithRestart()
 	if err != nil {
 		return nil, err
 	}
@@ -233,7 +247,7 @@ func (u *User) Update(password string, nogw bool, hostid uint32, admin bool) err
 	}
 	db.Save(u.dbUserModel)
 
-	err := Emit()
+	err := EmitWithRestart()
 	if err != nil {
 		return err
 	}
@@ -255,7 +269,7 @@ func (u *User) Delete() error {
 	})
 	db.Unscoped().Delete(u.dbUserModel)
 	logrus.Infof("user deleted: %s", u.GetUsername())
-	err = Emit()
+	err = EmitWithRestart()
 	if err != nil {
 		return err
 	}
@@ -271,7 +285,7 @@ func (u *User) ResetPassword(password string) error {
 		return fmt.Errorf("user password can not be updated %s: %v", u.Username, err)
 	}
 	db.Save(u.dbUserModel)
-	err = Emit()
+	err = EmitWithRestart()
 	if err != nil {
 		return err
 	}
@@ -310,7 +324,7 @@ func (u *User) Renew() error {
 	u.ServerSerialNumber = server.SerialNumber
 
 	db.Save(u.dbUserModel)
-	err = Emit()
+	err = EmitWithRestart()
 	if err != nil {
 		return err
 	}

+ 20 - 0
user_test.go

@@ -78,6 +78,25 @@ func TestCreateNewUser(t *testing.T) {
 		t.Fatalf("user creation expected to err but it didn't")
 	}
 
+	// Test username validation.
+	var usernametests = []struct {
+		username string
+		ok       bool
+	}{
+		{"asdf1240asfd", true},
+		{"asdf.asfd", true},
+		{"asdf12.12asfd", true},
+		{"asd1f-as4fd", false},
+		{"as0df a01sfd", false},
+		{"a6sdf_as1fd", true},
+	}
+
+	for _, tt := range usernametests {
+		_, err := ovpm.CreateNewUser(tt.username, "1234", false, 0, true)
+		if ok := (err == nil); ok != tt.ok {
+			t.Fatalf("expcted condition failed '%s': %v", tt.username, err)
+		}
+	}
 }
 
 func TestUserUpdate(t *testing.T) {
@@ -308,6 +327,7 @@ func TestUserIPAllocator(t *testing.T) {
 		{"user4", true, ovpm.IP2HostID(net.ParseIP("10.9.0.5").To4()), "10.9.0.5/24", true},
 		{"user6", true, ovpm.IP2HostID(net.ParseIP("10.9.0.7").To4()), "10.9.0.7/24", true},
 		{"user7", true, 0, "10.9.0.6/24", true},
+		{"user6", true, ovpm.IP2HostID(net.ParseIP("10.9.0.1").To4()), "10.9.0.7/24", false},
 	}
 	for _, tt := range iptests {
 		user, err := ovpm.CreateNewUser(tt.username, "pass", tt.gw, tt.hostid, true)

+ 14 - 4
vpn.go

@@ -267,7 +267,7 @@ func Init(hostname string, port string, proto string, ipblock string, dns string
 		user.HostID = 0
 		db.Save(&user.dbUserModel)
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("server initialized")
 	return nil
 }
@@ -313,7 +313,7 @@ func Update(ipblock string, dns string) error {
 			db.Save(user.dbUserModel)
 		}
 
-		Emit()
+		EmitWithRestart()
 		logrus.Infof("server updated")
 	}
 	return nil
@@ -327,7 +327,7 @@ func Deinit() error {
 
 	db.Unscoped().Delete(&dbServerModel{})
 	db.Unscoped().Delete(&dbRevokedModel{})
-	Emit()
+	EmitWithRestart()
 	return nil
 }
 
@@ -422,6 +422,7 @@ func StartVPNProc() {
 		logrus.Error("OpenVPN is already started")
 		return
 	}
+	Emit()
 	vpnProc.Start()
 	ensureNatEnabled()
 }
@@ -435,6 +436,7 @@ func RestartVPNProc() {
 	if vpnProc == nil {
 		panic(fmt.Sprintf("vpnProc is not initialized!"))
 	}
+	Emit()
 	vpnProc.Restart()
 	ensureNatEnabled()
 }
@@ -449,7 +451,6 @@ func StopVPNProc() {
 		return
 	}
 	vpnProc.Stop()
-
 }
 
 // Emit generates all needed files for the OpenVPN server and dumps them to their corresponding paths defined in the config.
@@ -509,7 +510,15 @@ func Emit() error {
 	}
 
 	logrus.Info("configurations emitted to the filesystem")
+	return nil
+}
 
+// EmitWithRestart restarts vpnProc after calling EmitWithRestart().
+func EmitWithRestart() error {
+	err := Emit()
+	if err != nil {
+		return err
+	}
 	if IsInitialized() {
 		for {
 			if vpnProc.Status() == supervisor.RUNNING || vpnProc.Status() == supervisor.STOPPED {
@@ -522,6 +531,7 @@ func Emit() error {
 	}
 
 	return nil
+
 }
 
 func emitToFile(path, content string, mode uint) error {

+ 4 - 0
webui/ovpm/app/api.js

@@ -44,6 +44,10 @@ export const endpoints = {
         path: "/vpn/status",
         method: "GET",
     },
+    vpnRestart: {
+        path: "/vpn/restart",
+        method: "POST",
+    },
     netDefine: {
         path: "/network/create",
         method: "POST",

+ 13 - 4
webui/ovpm/app/components/Dashboard/AdminDashboard/index.jsx

@@ -191,7 +191,6 @@ export default class AdminDashboard extends React.Component {
     }
 
     handleNewUserSave(user) {
-        console.log("HERE", user)
         let userObj = {
             username: user.username,
             password: user.password,
@@ -199,7 +198,6 @@ export default class AdminDashboard extends React.Component {
             host_id: 0, // handle this host_id problem
             is_admin: user.isAdmin,
         }
-        console.log("USER", user)
         userObj.no_gw = !user.pushGW
         userObj.admin_pref = user.isAdmin ? "ADMIN" : "NOADMIN"
         userObj.host_id = user.ipAllocationMethod === "static" ? dot2num(user.staticIP) : 0
@@ -292,7 +290,6 @@ export default class AdminDashboard extends React.Component {
     }
 
     handleDefineNetworkSave(network) {
-        console.log("NETWORK:", network)
         this.api.call("netDefine", network, true, this.handleDefineNetworkSuccess.bind(this), this.handleDefineNetworkFailure.bind(this))
         this.setState({modal: ""})
     }
@@ -372,7 +369,6 @@ export default class AdminDashboard extends React.Component {
     handleAssociateUserSave(username) {
         //call
         //refresh
-        console.log(username)
         this.api.call("netAssociate", {name: this.state.assocNetworkName, username : username}, true, this.handleAssociateUserSuccess.bind(this), this.handleAssociateUserFailure.bind(this))
         this.setState({modal: ""})
     }
@@ -406,6 +402,18 @@ export default class AdminDashboard extends React.Component {
         console.log(error)
     }
 
+    handleRestartVPNServer() {
+        this.api.call("vpnRestart", {}, true, function() {
+            this.refresh()
+        }.bind(this), function() {
+            if ('response' in error && error.response.status == 401) {
+                this.handleAuthFailure(error)
+            }
+            console.log(error)
+        }.bind(this))
+    }
+
+
     handleLogout() {
         ClearAuthToken()
         this.setState({logout: true})
@@ -541,6 +549,7 @@ export default class AdminDashboard extends React.Component {
                                     </table>
                                 </Tab>
                                 <Tab value="vpn" label="VPN">
+                                    <Button className="mui--pull-right" color="primary" onClick={this.handleRestartVPNServer.bind(this)}>Restart VPN Server</Button>
                                     <table className="mui-table mui-table--bordered mui--text-justify">
                                         <thead>
                                             <tr>

File diff suppressed because it is too large
+ 0 - 0
webui/ovpm/public/bundle.js


Some files were not shown because too many files changed in this diff