Skip to content

Commit fdc225c

Browse files
authored
Merge pull request #40 from inhogog2/main
Update Go version and add authentication support
2 parents 90cb61c + 91042e4 commit fdc225c

File tree

8 files changed

+417
-7
lines changed

8 files changed

+417
-7
lines changed

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ loxicmd aim to provide all of the configuation for the loxilb.`,
8585
rootCmd.PersistentFlags().StringVarP(&restOptions.PrintOption, "output", "o", "", "Set output layer (ex.) wide, json)")
8686
rootCmd.PersistentFlags().StringVarP(&restOptions.ServerIP, "apiserver", "s", "127.0.0.1", "Set API server IP address")
8787
rootCmd.PersistentFlags().Int16VarP(&restOptions.ServerPort, "port", "p", 11111, "Set API server port number")
88+
rootCmd.PersistentFlags().StringVarP(&restOptions.Token, "token", "", "", "Set Token for the API server")
8889

8990
rootCmd.AddCommand(get.GetCmd(restOptions))
9091
rootCmd.AddCommand(create.CreateCmd(restOptions))

cmd/set/set.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type SetResult struct {
2929
Result string `json:"result"`
3030
}
3131

32+
type SetOptions struct {
33+
Provider string
34+
}
35+
3236
// SetParamCmd represents the Set command
3337
func SetParamCmd(restOptions *api.RESTOptions) *cobra.Command {
3438
SetParamCmd := &cobra.Command{
@@ -42,6 +46,10 @@ func SetParamCmd(restOptions *api.RESTOptions) *cobra.Command {
4246
}
4347
SetParamCmd.AddCommand(NewSetLogLevelCmd(restOptions))
4448
SetParamCmd.AddCommand(NewSetBFDCmd(restOptions))
49+
SetParamCmd.AddCommand(NewSetLogInCmd(restOptions))
50+
SetParamCmd.AddCommand(NewSetLogOutCmd(restOptions))
51+
SetParamCmd.AddCommand(NewSetRefreshTokenCmd(restOptions))
52+
4553
return SetParamCmd
4654
}
4755

cmd/set/set_login.go

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/*
2+
* Copyright (c) 2025 LoxiLB Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package set
17+
18+
import (
19+
"bufio"
20+
"context"
21+
"encoding/json"
22+
"fmt"
23+
"io"
24+
"loxicmd/pkg/api"
25+
"net/http"
26+
"os"
27+
"strings"
28+
"time"
29+
30+
"github.com/spf13/cobra"
31+
"golang.org/x/term"
32+
)
33+
34+
// NewLogLevelCmd represents the save command
35+
func NewSetLogInCmd(restOptions *api.RESTOptions) *cobra.Command {
36+
SetOptions := SetOptions{}
37+
38+
var loginCmd = &cobra.Command{
39+
40+
Use: "login",
41+
Short: "login and set token",
42+
43+
Run: func(cmd *cobra.Command, args []string) {
44+
o := api.LoginModel{}
45+
if SetOptions.Provider == "" {
46+
reader := bufio.NewReader(os.Stdin)
47+
fmt.Print("Enter ID: ")
48+
userID, err := reader.ReadString('\n')
49+
if err != nil {
50+
fmt.Println("ID error:", err)
51+
return
52+
}
53+
userID = strings.TrimSpace(userID)
54+
fmt.Print("Enter Password: ")
55+
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
56+
if err != nil {
57+
fmt.Println("\nPassword error:", err)
58+
return
59+
}
60+
61+
// Make loginModel
62+
if err := ReadSetLogInOptions(&o, userID, bytePassword); err != nil {
63+
fmt.Printf("Error: %s\n", err.Error())
64+
return
65+
} // API Call
66+
67+
resp, err := LoginAPICall(restOptions, o)
68+
if err != nil {
69+
fmt.Printf("Error: %s\n", err.Error())
70+
return
71+
}
72+
// Save token
73+
// save the token in the file tmp/token.json
74+
if resp.StatusCode == http.StatusOK {
75+
PrintAndSaveTokenResult(resp, *restOptions)
76+
return
77+
}
78+
} else if SetOptions.Provider == "google" {
79+
// Google Login at first
80+
fmt.Printf("This process is how to log in with Google oauth. When you log in to the GUI you can get a token (access token) and a refresh token. After that, it is a registration process.\n")
81+
reader := bufio.NewReader(os.Stdin)
82+
fmt.Print("Enter Access Token: ")
83+
AccessToken, err := reader.ReadString('\n')
84+
if err != nil {
85+
fmt.Println("Access Token error:", err)
86+
return
87+
}
88+
AccessToken = strings.TrimSpace(AccessToken)
89+
fmt.Print("Enter Refresh Token: ")
90+
RefreshToken, err := reader.ReadString('\n')
91+
if err != nil {
92+
fmt.Println("Refresh token error", err)
93+
return
94+
}
95+
RefreshToken = strings.TrimSpace(RefreshToken)
96+
fmt.Printf("AccessToken: %v\n", AccessToken)
97+
fmt.Printf("RefreshToken: %v\n", RefreshToken)
98+
tokenFilePath := "/tmp/loxilbtoken"
99+
err = os.WriteFile(tokenFilePath, []byte(AccessToken), 0644)
100+
if err != nil {
101+
fmt.Printf("Error: Failed to write token to file: (%s)\n", err.Error())
102+
return
103+
}
104+
refreshTokenFilePath := "/tmp/loxilbrefreshtoken"
105+
err = os.WriteFile(refreshTokenFilePath, []byte(RefreshToken), 0644)
106+
if err != nil {
107+
fmt.Printf("Error: Failed to write token to file: (%s)\n", err.Error())
108+
return
109+
}
110+
fmt.Println("Login Success")
111+
112+
} else if SetOptions.Provider == "manual" {
113+
reader := bufio.NewReader(os.Stdin)
114+
fmt.Print("Enter Access Token: ")
115+
AccessToken, err := reader.ReadString('\n')
116+
if err != nil {
117+
fmt.Println("Access Token error:", err)
118+
return
119+
}
120+
AccessToken = strings.TrimSpace(AccessToken)
121+
tokenFilePath := "/tmp/loxilbtoken"
122+
err = os.WriteFile(tokenFilePath, []byte(AccessToken), 0644)
123+
if err != nil {
124+
fmt.Printf("Error: Failed to write token to file: (%s)\n", err.Error())
125+
return
126+
}
127+
fmt.Println("Login Success")
128+
129+
} else {
130+
fmt.Println("Error: Invalid provider name")
131+
return
132+
}
133+
134+
},
135+
}
136+
loginCmd.Flags().StringVarP(&SetOptions.Provider, "provider", "", "", "Define the provider name ex) google, manual")
137+
138+
return loginCmd
139+
}
140+
141+
func ReadSetLogInOptions(o *api.LoginModel, userID string, password []byte) error {
142+
o.Username = userID
143+
o.Password = string(password)
144+
return nil
145+
}
146+
147+
func LoginAPICall(restOptions *api.RESTOptions, loginModel api.LoginModel) (*http.Response, error) {
148+
client := api.NewLoxiClient(restOptions)
149+
ctx := context.TODO()
150+
var cancel context.CancelFunc
151+
if restOptions.Timeout > 0 {
152+
ctx, cancel = context.WithTimeout(context.TODO(), time.Duration(restOptions.Timeout)*time.Second)
153+
defer cancel()
154+
}
155+
156+
return client.Login().Create(ctx, loginModel)
157+
}
158+
159+
func PrintAndSaveTokenResult(resp *http.Response, o api.RESTOptions) {
160+
Tokenresp := api.TokenModel{}
161+
resultByte, err := io.ReadAll(resp.Body)
162+
if err != nil {
163+
fmt.Printf("Error: Failed to read HTTP response: (%s)\n", err.Error())
164+
return
165+
}
166+
167+
if err := json.Unmarshal(resultByte, &Tokenresp); err != nil {
168+
fmt.Printf("Error: Failed to unmarshal HTTP response: (%s)\n", err.Error())
169+
return
170+
}
171+
172+
if Tokenresp.Token == "" {
173+
fmt.Println("Error: Failed to get token Please check your ID or Password")
174+
return
175+
}
176+
177+
// Save the token to /tmp/loxilbtoken
178+
tokenFilePath := "/tmp/loxilbtoken"
179+
err = os.WriteFile(tokenFilePath, []byte(Tokenresp.Token), 0644)
180+
if err != nil {
181+
fmt.Printf("Error: Failed to write token to file: (%s)\n", err.Error())
182+
return
183+
}
184+
185+
// if json options enable, it print as a json format.
186+
if o.PrintOption == "json" {
187+
resultIndent, _ := json.MarshalIndent(Tokenresp, "", " ")
188+
fmt.Println(string(resultIndent))
189+
return
190+
}
191+
fmt.Println("Login Success")
192+
}
193+
194+
// NewLogLevelCmd represents the save command
195+
func NewSetLogOutCmd(restOptions *api.RESTOptions) *cobra.Command {
196+
SetOptions := SetOptions{}
197+
var logoutCmd = &cobra.Command{
198+
199+
Use: "logout",
200+
Short: "logout and remove token",
201+
202+
Run: func(cmd *cobra.Command, args []string) {
203+
if SetOptions.Provider == "" {
204+
resp, err := LogOutAPICall(restOptions)
205+
if err != nil {
206+
fmt.Printf("Error: %s\n", err.Error())
207+
return
208+
}
209+
// Remove token
210+
// Remove the token in the file tmp/token.json
211+
if resp.StatusCode == http.StatusOK {
212+
PrintAndRemoveTokenResult()
213+
return
214+
}
215+
} else if SetOptions.Provider == "google" {
216+
PrintAndRemoveRefreshTokenResult()
217+
PrintAndRemoveTokenResult()
218+
} else if SetOptions.Provider == "manual" {
219+
PrintAndRemoveTokenResult()
220+
} else {
221+
fmt.Println("Error: Invalid provider name")
222+
return
223+
}
224+
225+
},
226+
}
227+
228+
logoutCmd.Flags().StringVarP(&SetOptions.Provider, "provider", "", "", "Define the provider name ex) google, manual")
229+
230+
return logoutCmd
231+
}
232+
233+
func LogOutAPICall(restOptions *api.RESTOptions) (*http.Response, error) {
234+
client := api.NewLoxiClient(restOptions)
235+
ctx := context.TODO()
236+
var cancel context.CancelFunc
237+
if restOptions.Timeout > 0 {
238+
ctx, cancel = context.WithTimeout(context.TODO(), time.Duration(restOptions.Timeout)*time.Second)
239+
defer cancel()
240+
}
241+
// Call the Logout api. It will delete the token file in the DB.
242+
// It is create function, but it is actually delete function.
243+
return client.Login().SetUrl("/auth/logout").Create(ctx, nil)
244+
}
245+
246+
func PrintAndRemoveTokenResult() {
247+
// Delete the token to /tmp/loxilbtoken
248+
tokenFilePath := "/tmp/loxilbtoken"
249+
err := os.Remove(tokenFilePath)
250+
if err != nil {
251+
fmt.Printf("Error: Failed to remove token file: (%s)\n", err.Error())
252+
return
253+
}
254+
fmt.Println("Logout Success")
255+
}
256+
257+
func PrintAndRemoveRefreshTokenResult() {
258+
// Delete the token to /tmp/loxilbtoken
259+
tokenFilePath := "/tmp/loxilbrefreshtoken"
260+
err := os.Remove(tokenFilePath)
261+
if err != nil {
262+
fmt.Printf("Error: Failed to remove refreshtoken file: (%s)\n", err.Error())
263+
return
264+
}
265+
}
266+
267+
// NewLogLevelCmd represents the save command
268+
func NewSetRefreshTokenCmd(restOptions *api.RESTOptions) *cobra.Command {
269+
SetOptions := SetOptions{}
270+
271+
var loginCmd = &cobra.Command{
272+
273+
Use: "refresh",
274+
Short: "refresh token",
275+
276+
Run: func(cmd *cobra.Command, args []string) {
277+
if SetOptions.Provider == "google" {
278+
// Get the token from the file /tmp/loxilbtoken
279+
tokenFilePath := "/tmp/loxilbtoken"
280+
tokenByte, err := os.ReadFile(tokenFilePath)
281+
if err != nil {
282+
fmt.Printf("Error: Failed to read token file: (%s)\n", err.Error())
283+
return
284+
}
285+
// Get the refresh token from the file /tmp/loxilbrefreshtoken
286+
refreshTokenFilePath := "/tmp/loxilbrefreshtoken"
287+
refreshTokenByte, err := os.ReadFile(refreshTokenFilePath)
288+
if err != nil {
289+
fmt.Printf("Error: Failed to read refreshtoken file: (%s)\n", err.Error())
290+
return
291+
}
292+
o := api.TokenModel{}
293+
o.Token = string(tokenByte)
294+
o.RefreshToken = string(refreshTokenByte)
295+
296+
resp, err := RefreshTokenAPICall(restOptions, o)
297+
if err != nil {
298+
fmt.Printf("Error: %s\n", err.Error())
299+
return
300+
}
301+
// Save token
302+
// save the token in the file tmp/token.json
303+
if resp.StatusCode == http.StatusOK {
304+
PrintAndSaveTokenResult(resp, *restOptions)
305+
return
306+
} else if resp.StatusCode == http.StatusForbidden {
307+
fmt.Println("Error: Failed to refresh token. Please login again")
308+
return
309+
} else {
310+
fmt.Printf("resp.StatusCode: %v\n", resp.StatusCode)
311+
fmt.Println("Error: Failed to refresh token")
312+
return
313+
}
314+
} else {
315+
fmt.Println("Error: Invalid provider name")
316+
return
317+
}
318+
319+
},
320+
}
321+
loginCmd.Flags().StringVarP(&SetOptions.Provider, "provider", "", "", "Define the provider name ex)google, github, manual")
322+
323+
return loginCmd
324+
}
325+
326+
func RefreshTokenAPICall(restOptions *api.RESTOptions, TokenModel api.TokenModel) (*http.Response, error) {
327+
client := api.NewLoxiClient(restOptions)
328+
ctx := context.TODO()
329+
var cancel context.CancelFunc
330+
if restOptions.Timeout > 0 {
331+
ctx, cancel = context.WithTimeout(context.TODO(), time.Duration(restOptions.Timeout)*time.Second)
332+
defer cancel()
333+
}
334+
queryArgs := make(map[string]string)
335+
queryArgs["token"] = TokenModel.Token
336+
queryArgs["refreshtoken"] = TokenModel.RefreshToken
337+
return client.Login().SetUrl("oauth/google/token").Query(queryArgs).Get(ctx)
338+
}

go.mod

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
module loxicmd
22

3-
go 1.18
3+
go 1.23.0
4+
5+
toolchain go1.23.7
46

57
require (
68
github.com/olekukonko/tablewriter v0.0.5
79
github.com/spf13/cobra v1.5.0
810
github.com/vishvananda/netlink v1.1.0
9-
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
11+
golang.org/x/sys v0.31.0
12+
golang.org/x/term v0.30.0
1013
gopkg.in/yaml.v2 v2.4.0
1114
)
1215

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
2222
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
2323
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
2424
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
25-
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
26-
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
25+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
26+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
27+
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
28+
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
2729
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2830
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
2931
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

0 commit comments

Comments
 (0)