Skip to content

Commit c8b7bc0

Browse files
Added JWT authentication functions and models. Initiated ENV Variables for secrets. Formated go code.
1 parent c84b83c commit c8b7bc0

File tree

11 files changed

+175
-118
lines changed

11 files changed

+175
-118
lines changed

cmd/laclm/main.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ func exec() error {
2626

2727
/* exec() wraps run() protecting it with user interrupts */
2828

29-
/*
30-
load config file
29+
/*
30+
load config file
3131
if there is an error in loading the config file, then it will exit with code 1
3232
*/
3333
config.LoadConfig("./config.yaml")
@@ -62,22 +62,22 @@ func run(ctx context.Context) error {
6262
routes.RegisterRoutes(mux)
6363

6464
server := &http.Server{
65-
Addr: fmt.Sprintf("%s:%s",
65+
Addr: fmt.Sprintf("%s:%s",
6666
config.BackendConfig.Server.Host,
67-
config.BackendConfig.Server.Port,
67+
config.BackendConfig.Server.Port,
6868
),
69-
Handler: mux,
70-
}
69+
Handler: mux,
70+
}
7171

7272
/* starting http server as a goroutine */
7373
go func() {
74-
zap.L().Info("HTTP REST API server starting on :8080")
74+
zap.L().Info("HTTP REST API server starting on :8080")
7575
if err = server.ListenAndServe(); err != http.ErrServerClosed {
76-
zap.L().Error("ListenAndServe error",
76+
zap.L().Error("ListenAndServe error",
7777
zap.Error(err),
7878
)
79-
}
80-
}()
79+
}
80+
}()
8181

8282
/*
8383
whatever written here will be protected by graceful shutdowns
@@ -86,24 +86,24 @@ func run(ctx context.Context) error {
8686

8787
<-ctx.Done()
8888

89-
/*
89+
/*
9090
after this, exit signal is triggered
9191
following code must be executed to shutdown graceful shutdown
9292
call all the kill switches with context
9393
*/
94-
94+
9595
/* graceful shutdown of http server */
9696
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
9797
defer shutdownCancel()
9898

9999
/* initiate http server shutdown */
100100
if err = server.Shutdown(shutdownCtx); err != nil {
101-
zap.L().Error("HTTP server shutdown error",
101+
zap.L().Error("HTTP server shutdown error",
102102
zap.Error(err),
103103
)
104-
}
105-
104+
}
105+
106106
zap.L().Info("HTTP server stopped")
107107

108-
return err
108+
return err
109109
}

config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ filesystem_servers:
3737
# authentication information
3838
authentication:
3939
ldap:
40+
41+
backend_security:
42+
jwt_expiry:

config/config.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package config
22

3-
import "github.com/PythonHacker24/linux-acl-management-backend/internal/models"
3+
import (
4+
"github.com/PythonHacker24/linux-acl-management-backend/internal/models"
5+
)
46

5-
/* globally accessible config */
6-
var BackendConfig models.Config
7+
/* globally accessible yaml config */
8+
var BackendConfig models.Config
9+
10+
/* globally accessible environment variables */
11+
var EnvConfig models.EnvConfig

config/loader.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,30 @@ import (
1111
func LoadConfig(path string) {
1212

1313
/* read the yaml config file */
14-
data, err := os.ReadFile(path)
15-
if err != nil {
16-
zap.L().Fatal("Failed to read config file",
14+
data, err := os.ReadFile(path)
15+
if err != nil {
16+
zap.L().Fatal("Failed to read config file",
1717
zap.Error(err),
1818
)
19-
}
19+
}
2020

2121
/* unmarshal the yaml file to defined struct */
22-
err = yaml.Unmarshal(data, &BackendConfig)
23-
if err != nil {
24-
zap.L().Fatal("Failed to parse YAML config",
22+
err = yaml.Unmarshal(data, &BackendConfig)
23+
if err != nil {
24+
zap.L().Fatal("Failed to parse YAML config",
2525
zap.Error(err),
2626
)
27-
}
27+
}
28+
}
29+
30+
/* loads environment variables */
31+
func LoadEnv() {
32+
33+
/* get the JWT_SECRET_KEY from environment variable */
34+
secret := os.Getenv("JWT_SECRET_KEY")
35+
if secret == "" {
36+
zap.L().Fatal("JWT_SECRET_KEY environment variable not set")
37+
}
38+
39+
EnvConfig.JWTSecret = secret
2840
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/bufbuild/buf v1.53.0 // indirect
88
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
99
github.com/go-ldap/ldap/v3 v3.4.11 // indirect
10+
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
1011
github.com/google/uuid v1.6.0 // indirect
1112
go.uber.org/multierr v1.11.0 // indirect
1213
go.uber.org/zap v1.27.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ
66
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
77
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
88
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
9+
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
10+
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
911
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
1012
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
1113
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package authentication
2+
3+
import (
4+
"time"
5+
6+
"github.com/PythonHacker24/linux-acl-management-backend/config"
7+
"github.com/golang-jwt/jwt/v5"
8+
)
9+
10+
/* generating jwt token for user identification with specified configs */
11+
func GenerateJWT(username string) (string, error) {
12+
expiryHours := config.BackendConfig.BackendSecurity.JWTExpiry
13+
if expiryHours == 0 {
14+
expiryHours = 24
15+
}
16+
17+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
18+
"username": username,
19+
"exp": time.Now().Add(time.Hour * time.Duration(expiryHours)).Unix(),
20+
})
21+
22+
return token.SignedString([]byte(config.EnvConfig.JWTSecret))
23+
}

internal/handlers/handlers.go

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package handlers
22

33
import (
4-
"net/http"
54
"encoding/json"
5+
"net/http"
66

77
"go.uber.org/zap"
88

@@ -12,16 +12,16 @@ import (
1212
/* health handler provides status check on the backend server */
1313
func HealthHandler(w http.ResponseWriter, r *http.Request) {
1414
var response models.HealthResponse
15-
16-
w.Header().Set("Content-Type", "application/json")
17-
w.WriteHeader(http.StatusOK)
1815

19-
response.Status = "ok"
20-
if err := json.NewEncoder(w).Encode(response); err != nil {
21-
zap.L().Error("Failed to send health response from the handler",
16+
w.Header().Set("Content-Type", "application/json")
17+
w.WriteHeader(http.StatusOK)
18+
19+
response.Status = "ok"
20+
if err := json.NewEncoder(w).Encode(response); err != nil {
21+
zap.L().Error("Failed to send health response from the handler",
2222
zap.Error(err),
2323
)
24-
}
24+
}
2525
}
2626

2727
/* allows users to create a session */
@@ -30,48 +30,48 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
3030

3131
/* decode json response*/
3232
err := json.NewDecoder(r.Body).Decode(&user)
33-
if err != nil {
33+
if err != nil {
3434
zap.L().Warn("A request with invalid body recieved")
35-
http.Error(w, "Invalid request body", http.StatusBadRequest)
36-
return
37-
}
35+
http.Error(w, "Invalid request body", http.StatusBadRequest)
36+
return
37+
}
3838

3939
/* check if username and password exists */
4040
if user.Username == "" || user.Password == "" {
4141
zap.L().Warn("A request with no username or password recieved")
42-
http.Error(w, "Username and password are required", http.StatusBadRequest)
43-
return
44-
}
42+
http.Error(w, "Username and password are required", http.StatusBadRequest)
43+
return
44+
}
4545

4646
/* authenticate the user with ldap */
4747
authStatus := ldap.AuthenticateUser()
48-
if !authStatus {
48+
if !authStatus {
4949
zap.L().Warn("A request with invalid credentials recieved")
50-
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
51-
return
52-
}
50+
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
51+
return
52+
}
5353

5454
/* create a session if user exists */
5555
sessionmanager.CreateSession(user.Username)
5656

5757
/* create a JWT token for the user */
5858
token, err := authentication.GenerateJWT(user.Username)
59-
if err != nil {
60-
zap.L().Error("Error generating token",
59+
if err != nil {
60+
zap.L().Error("Error generating token",
6161
zap.Error(err),
6262
)
63-
http.Error(w, "Error generating token", http.StatusInternalServerError)
64-
return
65-
}
63+
http.Error(w, "Error generating token", http.StatusInternalServerError)
64+
return
65+
}
6666

6767
/* send response with JWT token to the user */
6868
response := map[string]string{"token": token}
69-
w.Header().Set("Content-Type", "application/json")
70-
if err := json.NewEncoder(w).Encode(response); err != nil {
71-
zap.L().Error("Failed to encode response",
69+
w.Header().Set("Content-Type", "application/json")
70+
if err := json.NewEncoder(w).Encode(response); err != nil {
71+
zap.L().Error("Failed to encode response",
7272
zap.Error(err),
7373
)
74-
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
75-
return
76-
}
74+
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
75+
return
76+
}
7777
}

internal/ldap/ldap.go

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package ldap
22

33
import (
44
"fmt"
5-
"go.uber.org/zap"
5+
"go.uber.org/zap"
66

77
"github.com/go-ldap/ldap/v3"
88
)
@@ -20,70 +20,70 @@ func AuthenticateUser(username, password, searchbase string) bool {
2020

2121
/* dial to the ldap server */
2222
l, err := ldap.DialURL("")
23-
if err != nil {
24-
zap.L().Error("Failed to connect to LDAP Server",
23+
if err != nil {
24+
zap.L().Error("Failed to connect to LDAP Server",
2525
zap.Error(err),
2626
)
27-
return false
28-
}
29-
defer l.Close()
30-
27+
return false
28+
}
29+
defer l.Close()
30+
3131
/* authenticating with the ldap server with admin */
3232
err = l.Bind("", "")
33-
if err != nil {
34-
zap.L().Error("Admin authentication failed",
33+
if err != nil {
34+
zap.L().Error("Admin authentication failed",
3535
zap.Error(err),
3636
)
37-
return false
38-
}
37+
return false
38+
}
3939

4040
/* creating a search request for ldap server */
4141
searchRequest := ldap.NewSearchRequest(
42-
searchbase,
43-
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
42+
searchbase,
43+
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
4444

4545
/* Searching by username */
46-
fmt.Sprintf("(uid=%s)", username),
47-
46+
fmt.Sprintf("(uid=%s)", username),
47+
4848
/* We only need the DN */
49-
[]string{"dn"},
50-
nil,
51-
)
49+
[]string{"dn"},
50+
nil,
51+
)
5252

5353
/* searching the ldap server for credentials */
5454
searchResult, err := l.Search(searchRequest)
55-
if err != nil {
56-
zap.L().Error("LDAP search failed",
55+
if err != nil {
56+
zap.L().Error("LDAP search failed",
5757
zap.Error(err),
5858
)
59-
return false
60-
}
59+
return false
60+
}
6161

6262
/* checking if search result is empty */
6363
if len(searchResult.Entries) == 0 {
64-
zap.L().Error("User not found in LDAP",
64+
zap.L().Error("User not found in LDAP",
6565
zap.String("username", username),
6666
zap.Error(err),
6767
)
68-
return false
69-
}
68+
return false
69+
}
7070

7171
userDN := searchResult.Entries[0].DN
7272

7373
/* checking if the user exists */
7474
err = l.Bind(userDN, password)
75-
if err != nil {
76-
zap.L().Error("User authentication failed",
75+
if err != nil {
76+
zap.L().Error("User authentication failed",
7777
zap.String("Username", username),
7878
zap.Error(err),
7979
)
80-
return false
81-
}
80+
return false
81+
}
8282

8383
/* authentication successful */
84-
zap.L().Info("User authentication successful",
84+
zap.L().Info("User authentication successful",
8585
zap.String("username", username),
8686
)
8787

88-
return true
88+
return true
8989
}

0 commit comments

Comments
 (0)