Skip to content

Conversation

mundele2004
Copy link

@mundele2004 mundele2004 commented Jul 17, 2025

Pull Request Template

Description:

  • Added a plug-and-play LDAP authentication module under internal/auth/ldapauth.
  • The module performs Bind(username, password) authentication against an LDAP server and issues a JWT token upon successful login.
  • Addresses the LDAP Integration proposal for GoFr Framework Extension as per SOC.
  • Closes: issue LDAP Integration Helper Module #2014

Breaking Changes (if applicable):

  • None. This module is self-contained and does not interfere with any existing authentication mechanisms or routing middleware.

Additional Information:

  • External Library used: github.com/go-ldap/ldap/v3
  • JWT is issued using standard library packages (crypto/rand, encoding/base64) with HMAC SHA256 signing.
  • Unit tests include edge cases using a mock LDAP dialer function.
  • See mainFile/main.go for demo and testuser.ldif for testing against a local OpenLDAP instance.
  • Sample test user:

Summary

This PR introduces a standalone LDAP authentication module (internal/auth/ldapauth) for the GoFr framework. It uses Bind(username, password) to validate users and issues a JWT on successful login.


What's Included

  • ldapauth.go: Core logic for LDAP authentication and JWT issuance
  • ldapauth_test.go: Unit tests with mock LDAP dialer
  • main.go: Demo login flow using /login route
  • testuser.ldif: Sample LDAP user for testing

How to Test

  1. Run local LDAP server (e.g., using Docker: osixia/openldap)
  2. Load test user using testuser.ldif:
    docker cp mainFile/testuser.ldif myldap:/testuser.ldif
    docker exec -it myldap ldapadd -x -D "cn=admin,dc=example,dc=com" -w admin -f /testuser.ldif
  3. Run the server:- go run mainFile/main.go
  4. Test login:
    curl -X POST http://localhost:8080/login
    -H "Content-Type: application/json"
    -d '{"username":"testuser", "password":"testpass"}'

Checklist:

  • I have formatted my code using goimport and golangci-lint.
  • All new code is covered by unit tests.
  • This PR does not decrease the overall code coverage.
  • I have reviewed the code comments and documentation for clarity.

@mundele2004 mundele2004 marked this pull request as ready for review July 17, 2025 16:22
Copy link
Contributor

@gizmo-rt gizmo-rt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of Test cases to add

  1. LDAP destination does not exists
  2. . Bind request from an unauthorised user
  3. Binding fail due to invalid username/pass
  4. Binding Failure due to expired password
  5. BindSuccess but info retrieval failed
  6. App unable to generate JWT
  7. Using an expired JWT


func (a *Authenticator) Authenticate(username, password string) (string, error) {
// l, err := ldap.DialURL("ldap://" + a.cfg.Addr)
l, err := a.dialFn("ldap://" + a.cfg.Addr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on what l is ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, was wondering what possible validations we could have for a.cfg.Addr ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l is the LDAP connection (*ldap.Conn) returned by the dialer.

We should validate a.cfg.Addr for non-empty and proper host:port format using net.SplitHostPort.


func New(cfg Config) *Authenticator {
return &Authenticator{
cfg: cfg,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the config specific to the LDAP library being used ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Config struct here is not directly from the go-ldap/ldap/v3 library — it's a custom wrapper defined within this module to keep the Authenticator decoupled from external configs.

This design allows flexibility — if we change or extend the backend (e.g. add StartTLS, change port or baseDN logic), we just update our config struct without impacting the rest of the app.

// Config holds LDAP server connection details and base search parameters.
// It is application-defined and not tied to the go-ldap library.

type Config struct {
      Addr     string
      BaseDN   string
      BindDN   string
      BindPass string
}

Comment on lines 55 to 79
searchReq := ldap.NewSearchRequest(
a.cfg.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(uid=%s)", ldap.EscapeFilter(username)),
[]string{"dn"},
nil,
)

sr, err := l.Search(searchReq)
if err != nil || len(sr.Entries) != 1 {
return "", fmt.Errorf("user not found")
}

userDN := sr.Entries[0].DN

if err := l.Bind(userDN, password); err != nil {
return "", fmt.Errorf("invalid credentials")
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": username,
"exp": time.Now().Add(1 * time.Hour).Unix(),
})

signed, err := token.SignedString([]byte(a.cfg.JWTSecret))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should preferably be decomposed into smaller, testable sections

@mundele2004
Copy link
Author

Hi @gizmo-rt 👋
✅ All edge cases tested and working:

  • Valid login
  • Invalid password
  • Unknown user
  • LDAP unreachable

Updated main.go + test cases as requested.

@gizmo-rt I’ve added the requested edge case tests. Let me know if any further changes are needed. ✅

✅ Added all suggested edge case tests including:

  • LDAP server unreachable
  • Unauthorized bind
  • Invalid credentials
  • Expired password simulation
  • DN found but user info retrieval failed
  • JWT generation failure
  • Expired JWT handling

🧪 All tests pass locally.

PR is ready for review. Thanks!

@mundele2004
Copy link
Author

@Umang01-hash @coolwednesday Is there any way or command I could use to run Code Quality checks on my local machine in the same way they are executed in the pipeline?

@Umang01-hash
Copy link
Member

@Umang01-hash @coolwednesday Is there any way or command I could use to run Code Quality checks on my local machine in the same way they are executed in the pipeline?

Hey @mundele2004! To run these code qaulity checks you can run golangci-lint run command on your local and see. Just make sure the linter version used in pipeline and the one installed on your system is same.

@mundele2004
Copy link
Author

✅ Resolved all golangci-lint issues and force-pushed updated changes.
All tests are passing locally. Awaiting review. 🚀
plz check @Umang01-hash

@mundele2004
Copy link
Author

Final PR after cleanup and linting. Ready for review 🚀
cc @akshat-kumar-singhal @Umang01-hash

@mundele2004
Copy link
Author

✅ Resolved all golangci-lint issues .
All tests are passing locally. . Ready for review 🚀
@akshat-kumar-singhal @Umang01-hash

@akshat-kumar-singhal
Copy link
Contributor

✅ Resolved all golangci-lint issues . All tests are passing locally. . Ready for review 🚀 @akshat-kumar-singhal @Umang01-hash

@mundele2004 Please address all the feedback/comments on the PR - don't mark them resolved without a response/change.

@mundele2004
Copy link
Author

Please check — I have fixed the linter issues such as whitespace, cuddled statements, etc.
You also check and let me know. If there are still 2 gci issues, please tell me how I can solve them, because I have tried a lot but am unable to fix them. Kindly guide me.

**$ golangci-lint run
internal\auth\ldapauth\ldapauth.go:7:1: File is not properly formatted (gci)
"github.com/go-ldap/ldap/v3"
^
internal\auth\ldapauth\ldapauth_test.go:7:1: File is not properly formatted (gci)
"github.com/go-ldap/ldap/v3"
^
2 issues:

  • gci: 2**

@akshat-kumar-singhal @Umang01-hash

@Umang01-hash
Copy link
Member

Please check — I have fixed the linter issues such as whitespace, cuddled statements, etc. You also check and let me know. If there are still 2 gci issues, please tell me how I can solve them, because I have tried a lot but am unable to fix them. Kindly guide me.

**$ golangci-lint run internal\auth\ldapauth\ldapauth.go:7:1: File is not properly formatted (gci) "github.com/go-ldap/ldap/v3" ^ internal\auth\ldapauth\ldapauth_test.go:7:1: File is not properly formatted (gci) "github.com/go-ldap/ldap/v3" ^ 2 issues:

  • gci: 2**

@akshat-kumar-singhal @Umang01-hash

@mundele2004 You can fix gci import order issues by installing and running the gci tool.

go install github.com/daixiang0/gci@latest

and then use gci write . inside the directory ldapauth. It should fix these errors.

@mundele2004
Copy link
Author

Please check — I have fixed the linter issues such as whitespace, cuddled statements, etc. You also check and let me know. If there are still 2 gci issues, please tell me how I can solve them, because I have tried a lot but am unable to fix them. Kindly guide me.
**$ golangci-lint run internal\auth\ldapauth\ldapauth.go:7:1: File is not properly formatted (gci) "github.com/go-ldap/ldap/v3" ^ internal\auth\ldapauth\ldapauth_test.go:7:1: File is not properly formatted (gci) "github.com/go-ldap/ldap/v3" ^ 2 issues:

  • gci: 2**

@akshat-kumar-singhal @Umang01-hash

@mundele2004 You can fix gci import order issues by installing and running the gci tool.

go install github.com/daixiang0/gci@latest

and then use gci write . inside the directory ldapauth. It should fix these errors.

@Umang01-hash I tried the steps you suggested, but those 2 issues are still not resolved. Please guide me on how to fix those errors.

@mundele2004
Copy link
Author

Please check — I have fixed the linter issues such as whitespace, cuddled statements, etc. You also check and let me know. If there are still 2 gci issues, please tell me how I can solve them, because I have tried a lot but am unable to fix them. Kindly guide me.
**$ golangci-lint run internal\auth\ldapauth\ldapauth.go:7:1: File is not properly formatted (gci) "github.com/go-ldap/ldap/v3" ^ internal\auth\ldapauth\ldapauth_test.go:7:1: File is not properly formatted (gci) "github.com/go-ldap/ldap/v3" ^ 2 issues:

  • gci: 2**

@akshat-kumar-singhal @Umang01-hash

@mundele2004 You can fix gci import order issues by installing and running the gci tool.

go install github.com/daixiang0/gci@latest

and then use gci write . inside the directory ldapauth. It should fix these errors.

@Umang01-hash I tried the steps you suggested, but those 2 issues are still not resolved. Please guide me on how to fix those errors.

@Umang01-hash
I have resolved all the issues, including the merge conflicts and the gci import order problems. I installed the gci tool and ran gci write . inside the ldapauth directory as suggested, and now golangci-lint passes without errors.

Everything is working fine now. ✅

@mundele2004
Copy link
Author

Please tell me what I should do to fix this:
Workflow-Pipeline / PKG Unit Testing (v1.22)🛠 (pull_request)
@Umang01-hash

@mundele2004
Copy link
Author

Please tell me what I should do to fix this:
Workflow-Pipeline / PKG Unit Testing (v1.22)🛠 (pull_request)
@Umang01-hash @akshat-kumar-singhal

1 similar comment
@mundele2004
Copy link
Author

Please tell me what I should do to fix this:
Workflow-Pipeline / PKG Unit Testing (v1.22)🛠 (pull_request)
@Umang01-hash @akshat-kumar-singhal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants