Compare commits

...

6 Commits

Author SHA1 Message Date
vx3r 4a6cc6dea8 update node modules 2020-06-10 16:55:20 +09:00
vx3r 6049ba8089 go fmt and golint 2020-06-10 16:52:44 +09:00
vx3r 172551dcab implement feature requests #43 and #44 2020-06-10 16:44:15 +09:00
vx3r 34a06ad258
Merge pull request #42 from TheLD6978/master
Add tags to clients
2020-05-28 15:42:16 +09:00
TheLD 9cd9f3e018 add ui for tags 2020-05-26 18:22:35 +02:00
TheLD ce7e90e034 add tags to clients 2020-05-26 16:42:37 +02:00
14 changed files with 939 additions and 1863 deletions

View File

@ -5,6 +5,7 @@ import (
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1" "gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1"
) )


// ApplyRoutes apply routes to gin engine
func ApplyRoutes(r *gin.Engine, private bool) { func ApplyRoutes(r *gin.Engine, private bool) {
api := r.Group("/api") api := r.Group("/api")
{ {

View File

@ -16,8 +16,8 @@ import (
func ApplyRoutes(r *gin.RouterGroup) { func ApplyRoutes(r *gin.RouterGroup) {
g := r.Group("/auth") g := r.Group("/auth")
{ {
g.GET("/oauth2_url", oauth2_url) g.GET("/oauth2_url", oauth2URL)
g.POST("/oauth2_exchange", oauth2_exchange) g.POST("/oauth2_exchange", oauth2Exchange)
g.GET("/user", user) g.GET("/user", user)
g.GET("/logout", logout) g.GET("/logout", logout)
} }
@ -26,7 +26,7 @@ func ApplyRoutes(r *gin.RouterGroup) {
/* /*
* generate redirect url to get OAuth2 code or let client know that OAuth2 is disabled * generate redirect url to get OAuth2 code or let client know that OAuth2 is disabled
*/ */
func oauth2_url(c *gin.Context) { func oauth2URL(c *gin.Context) {
cacheDb := c.MustGet("cache").(*cache.Cache) cacheDb := c.MustGet("cache").(*cache.Cache)


state, err := util.GenerateRandomString(32) state, err := util.GenerateRandomString(32)
@ -62,7 +62,7 @@ func oauth2_url(c *gin.Context) {
/* /*
* exchange code and get user infos, if OAuth2 is disable just send fake data * exchange code and get user infos, if OAuth2 is disable just send fake data
*/ */
func oauth2_exchange(c *gin.Context) { func oauth2Exchange(c *gin.Context) {
var loginVals model.Auth var loginVals model.Auth
if err := c.ShouldBind(&loginVals); err != nil { if err := c.ShouldBind(&loginVals); err != nil {
log.WithFields(log.Fields{ log.WithFields(log.Fields{

View File

@ -7,6 +7,7 @@ import (
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/server" "gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/server"
) )


// ApplyRoutes apply routes to gin router
func ApplyRoutes(r *gin.RouterGroup, private bool) { func ApplyRoutes(r *gin.RouterGroup, private bool) {
v1 := r.Group("/v1.0") v1 := r.Group("/v1.0")
{ {

View File

@ -11,6 +11,7 @@ import (
"os" "os"
) )


// Auth interface to implement as auth provider
type Auth interface { type Auth interface {
Setup() error Setup() error
CodeUrl(state string) string CodeUrl(state string) string
@ -18,6 +19,7 @@ type Auth interface {
UserInfo(oauth2Token *oauth2.Token) (*model.User, error) UserInfo(oauth2Token *oauth2.Token) (*model.User, error)
} }


// GetAuthProvider get an instance of auth provider based on config
func GetAuthProvider() (Auth, error) { func GetAuthProvider() (Auth, error) {
var oauth2Client Auth var oauth2Client Auth
var err error var err error

View File

@ -7,6 +7,7 @@ import (
"time" "time"
) )


// Fake in order to implement interface, struct is required
type Fake struct{} type Fake struct{}


// Setup validate provider // Setup validate provider

View File

@ -13,6 +13,7 @@ import (
"time" "time"
) )


// Github in order to implement interface, struct is required
type Github struct{} type Github struct{}


var ( var (

View File

@ -10,6 +10,7 @@ import (
"os" "os"
) )


// Oauth2idc in order to implement interface, struct is required
type Oauth2idc struct{} type Oauth2idc struct{}


var ( var (

View File

@ -1,5 +1,6 @@
package model package model


// Auth structure
type Auth struct { type Auth struct {
Oauth2 bool `json:"oauth2"` Oauth2 bool `json:"oauth2"`
ClientId string `json:"clientId"` ClientId string `json:"clientId"`

View File

@ -16,6 +16,7 @@ type Client struct {
PresharedKey string `json:"presharedKey"` PresharedKey string `json:"presharedKey"`
AllowedIPs []string `json:"allowedIPs"` AllowedIPs []string `json:"allowedIPs"`
Address []string `json:"address"` Address []string `json:"address"`
Tags []string `json:"tags"`
PrivateKey string `json:"privateKey"` PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"` PublicKey string `json:"publicKey"`
CreatedBy string `json:"createdBy"` CreatedBy string `json:"createdBy"`

View File

@ -2,6 +2,7 @@ package model


import "time" import "time"


// User structure
type User struct { type User struct {
Sub string `json:"sub"` Sub string `json:"sub"`
Name string `json:"name"` Name string `json:"name"`

2641
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,22 +8,22 @@
}, },
"dependencies": { "dependencies": {
"axios": "^0.19.2", "axios": "^0.19.2",
"is-cidr": "^3.1.0", "is-cidr": "^3.1.1",
"moment": "^2.24.0", "moment": "^2.26.0",
"vue": "^2.6.10", "vue": "^2.6.10",
"vue-axios": "^2.1.5", "vue-axios": "^2.1.5",
"vue-moment": "^4.1.0", "vue-moment": "^4.1.0",
"vue-router": "^3.1.6", "vue-router": "^3.3.2",
"vuetify": "^2.2.22", "vuetify": "^2.2.33",
"vuex": "^3.2.0" "vuex": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-router": "^4.3.1", "@vue/cli-plugin-router": "^4.4.1",
"@vue/cli-service": "^4.3.1", "@vue/cli-service": "^4.4.1",
"sass": "^1.26.3", "sass": "^1.26.8",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"vue-cli-plugin-vuetify": "^2.0.5", "vue-cli-plugin-vuetify": "^2.0.5",
"vue-template-compiler": "^2.6.10", "vue-template-compiler": "^2.6.10",
"vuetify-loader": "^1.3.0" "vuetify-loader": "^1.4.4"
} }
} }

View File

@ -44,6 +44,17 @@
{{ ip }} {{ ip }}
</v-chip> </v-chip>
</template> </template>
<template v-slot:item.tags="{ item }">
<v-chip
v-for="(tag, i) in item.tags"
:key="i"
color="blue-grey"
text-color="white"
>
<v-icon left>mdi-tag</v-icon>
{{ tag }}
</v-chip>
</template>
<template v-slot:item.created="{ item }"> <template v-slot:item.created="{ item }">
<v-row> <v-row>
<p>At {{ item.created | formatDate }} by {{ item.createdBy }}</p> <p>At {{ item.created | formatDate }} by {{ item.createdBy }}</p>
@ -55,30 +66,39 @@
</v-row> </v-row>
</template> </template>
<template v-slot:item.action="{ item }"> <template v-slot:item.action="{ item }">
<v-icon <v-row>
class="pr-1 pl-1" <v-icon
@click.stop="startUpdate(item)" class="pr-1 pl-1"
> @click.stop="startUpdate(item)"
mdi-square-edit-outline >
</v-icon> mdi-square-edit-outline
<v-icon </v-icon>
class="pr-1 pl-1" <v-icon
@click.stop="forceFileDownload(item)" class="pr-1 pl-1"
> @click.stop="forceFileDownload(item)"
mdi-cloud-download-outline >
</v-icon> mdi-cloud-download-outline
<v-icon </v-icon>
class="pr-1 pl-1" <v-icon
@click.stop="email(item)" class="pr-1 pl-1"
> @click.stop="email(item)"
mdi-email-send-outline >
</v-icon> mdi-email-send-outline
<v-icon </v-icon>
class="pr-1 pl-1" <v-icon
@click="remove(item)" class="pr-1 pl-1"
> @click="remove(item)"
mdi-trash-can-outline >
</v-icon> mdi-trash-can-outline
</v-icon>
<v-switch
dark
class="pr-1 pl-1"
color="success"
v-model="item.enable"
v-on:change="update(item)"
/>
</v-row>
</template> </template>


</v-data-table> </v-data-table>
@ -122,6 +142,17 @@
{{ ip }} {{ ip }}
</v-chip> </v-chip>
</v-card-text> </v-card-text>
<v-card-text class="text--primary">
<v-chip
v-for="(tag, i) in client.tags"
:key="i"
color="blue-grey"
text-color="white"
>
<v-icon left>mdi-tag</v-icon>
{{ tag }}
</v-chip>
</v-card-text>
<v-card-actions> <v-card-actions>
<v-tooltip bottom> <v-tooltip bottom>
<template v-slot:activator="{ on }"> <template v-slot:activator="{ on }">
@ -255,7 +286,26 @@
</v-chip> </v-chip>
</template> </template>
</v-combobox> </v-combobox>

<v-combobox
v-model="client.tags"
chips
hint="Write tag name and hit enter"
label="Tags"
multiple
dark
>
<template v-slot:selection="{ attrs, item, select, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close
@click="select"
@click:close="client.tags.splice(client.tags.indexOf(item), 1)"
>
<strong>{{ item }}</strong>&nbsp;
</v-chip>
</template>
</v-combobox>
<v-switch <v-switch
v-model="client.enable" v-model="client.enable"
color="red" color="red"
@ -360,6 +410,26 @@
</v-chip> </v-chip>
</template> </template>
</v-combobox> </v-combobox>
<v-combobox
v-model="client.tags"
chips
hint="Write tag name and hit enter"
label="Tags"
multiple
dark
>
<template v-slot:selection="{ attrs, item, select, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close
@click="select"
@click:close="client.tags.splice(client.tags.indexOf(item), 1)"
>
<strong>{{ item }}</strong>&nbsp;
</v-chip>
</template>
</v-combobox>
<v-switch <v-switch
v-model="client.ignorePersistentKeepalive" v-model="client.ignorePersistentKeepalive"
color="red" color="red"
@ -409,6 +479,7 @@
{ text: 'Name', value: 'name', }, { text: 'Name', value: 'name', },
{ text: 'Email', value: 'email', }, { text: 'Email', value: 'email', },
{ text: 'IP addresses', value: 'address', }, { text: 'IP addresses', value: 'address', },
{ text: 'Tags', value: 'tags', },
{ text: 'Created', value: 'created', sortable: false, }, { text: 'Created', value: 'created', sortable: false, },
{ text: 'Updated', value: 'updated', sortable: false, }, { text: 'Updated', value: 'updated', sortable: false, },
{ text: 'Actions', value: 'action', sortable: false, }, { text: 'Actions', value: 'action', sortable: false, },
@ -451,6 +522,7 @@
enable: true, enable: true,
allowedIPs: this.server.allowedips, allowedIPs: this.server.allowedips,
address: this.server.address, address: this.server.address,
tags: [],
} }
this.dialogCreate = true; this.dialogCreate = true;
}, },
@ -529,7 +601,7 @@
const url = window.URL.createObjectURL(new Blob([config])) const url = window.URL.createObjectURL(new Blob([config]))
const link = document.createElement('a') const link = document.createElement('a')
link.href = url link.href = url
link.setAttribute('download', 'wg0.conf') //or any other extension link.setAttribute('download', client.name.split(' ').join('-') + '.conf') //or any other extension
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
}, },

View File

@ -11,6 +11,7 @@ import (
) )


var ( var (
// AuthTokenHeaderName http header for token transport
AuthTokenHeaderName = "x-wg-gen-web-auth" AuthTokenHeaderName = "x-wg-gen-web-auth"
// RegexpEmail check valid email // RegexpEmail check valid email
RegexpEmail = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") RegexpEmail = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")