POST /api/rpc
Content-Type: application/json
{
"method": string, // RPC method name
"params": string[] // Method parameters
}{
"success": boolean, // Operation success status
"data": string[] // Response data or error messages
}Changes a user's password in the LDAP/ActiveDirectory server.
{
"method": "change-password",
"params": [
"username", // sAMAccountName
"currentPassword", // Current password for authentication
"newPassword" // New password to set
]
}{
"success": true,
"data": ["password changed successfully"]
}HTTP Status: 200 OK
{
"success": false,
"data": ["error message"]
}HTTP Status: 500 Internal Server Error
Common Validation Errors:
"the username can't be empty""the old password can't be empty""the new password can't be empty""the old password can't be same as the new one""the new password must be at least {N} characters long""the new password must contain at least {N} number(s)""the new password must contain at least {N} symbol(s)""the new password must contain at least {N} uppercase letter(s)""the new password must contain at least {N} lowercase letter(s)""the new password must not include the username"
{
"success": false,
"data": ["LDAP error message from simple-ldap-go"]
}HTTP Status: 500 Internal Server Error
Common LDAP Errors:
- Authentication failures (incorrect current password)
- User not found in LDAP directory
- Password policy violations from AD/LDAP server
- Connection errors to LDAP server
{
"success": false,
"data": ["method not found"]
}HTTP Status: 400 Bad Request
Location: internal/rpchandler/change_password.go:18-72
func (c *Handler) changePassword(params []string) ([]string, error) {
// 1. Validate parameter count
if len(params) != 3 {
return nil, ErrInvalidArgumentCount
}
// 2. Extract parameters
sAMAccountName := params[0]
currentPassword := params[1]
newPassword := params[2]
// 3. Empty field validation
// 4. Password match validation
// 5. Length validation (c.opts.MinLength)
// 6. Number validation (validators.MinNumbersInString)
// 7. Symbol validation (validators.MinSymbolsInString)
// 8. Uppercase validation (validators.MinUppercaseLettersInString)
// 9. Lowercase validation (validators.MinLowercaseLettersInString)
// 10. Username inclusion check (optional, based on c.opts.PasswordCanIncludeUsername)
// 11. LDAP password change operation
return []string{"password changed successfully"}, nil
}Location: internal/web/static/js/app.ts:134-188
form.onsubmit = async (e) => {
e.preventDefault();
// 1. Collect form values
const [username, oldPassword, newPassword] = fields.map((f) => f.getValue());
// 2. Validate all fields
const hasErrors = fields.map(({ validate }) => validate()).some((e) => e === true);
if (hasErrors) return;
// 3. Disable form during submission
toggleFields(false);
// 4. Make RPC request
const res = await fetch("/api/rpc", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
method: "change-password",
params: [username, oldPassword, newPassword]
})
});
// 5. Handle response
if (!res.ok) {
// Display error
} else {
// Show success state
form.style.display = "none";
successContainer.style.display = "block";
}
};All validation rules are configurable via environment variables or command-line flags:
| Requirement | Config Variable | Default | Backend Validator | Frontend Validator |
|---|---|---|---|---|
| Minimum Length | MIN_LENGTH / --min-length |
8 | len(password) >= minLength |
mustBeLongerThan(n) |
| Minimum Numbers | MIN_NUMBERS / --min-numbers |
1 | MinNumbersInString |
mustIncludeNumbers(n) |
| Minimum Symbols | MIN_SYMBOLS / --min-symbols |
1 | MinSymbolsInString |
mustIncludeSymbols(n) |
| Minimum Uppercase | MIN_UPPERCASE / --min-uppercase |
1 | MinUppercaseLettersInString |
mustIncludeUppercase(n) |
| Minimum Lowercase | MIN_LOWERCASE / --min-lowercase |
1 | MinLowercaseLettersInString |
mustIncludeLowercase(n) |
| Username Exclusion | PASSWORD_CAN_INCLUDE_USERNAME / --password-can-include-username |
false | !strings.Contains(username, password) |
mustNotIncludeUsername (conditional) |
ASCII Ranges (matching implementation in validators.go:14-23 and validators.ts:1-23):
!to/(ASCII 33-47):! " # $ % & ' ( ) * + , - . /:to@(ASCII 58-64):: ; < = > ? @[to`(ASCII 91-96): `[ \ ] ^ _ ``{to~(ASCII 123-126):{ | } ~
Total Special Characters: 32 symbols
Frontend Only:
mustMatchNewPassword: Ensures password confirmation matches new passwordmustNotMatchCurrentPassword: Prevents reusing current password
Backend:
- Implicitly validates via LDAP authentication (current password must be correct)
Location: internal/web/static/js/app.ts:76-91
Errors are displayed below each input field in real-time:
- Red border around input container
- Error messages in red text (text-xs text-red-400)
- Submit button disabled while errors exist
Location: internal/rpchandler/handler.go:33-46
All errors are wrapped in consistent JSON-RPC response format:
return c.Status(http.StatusInternalServerError).JSON(JSONRPCResponse{
Success: false,
Data: []string{err.Error()},
})- Current password required for all password change operations
- LDAP server performs authentication (no password storage in application)
- Password transmitted via HTTPS (enforced by LDAPS requirement)
- Users can only change their own password
- Readonly LDAP user has minimal permissions (read-only access)
- No administrative privileges exposed via API
- Request body size limited to 4KB (main.go:31)
- All parameters validated before LDAP operation
- No SQL injection risk (LDAP-based, not SQL)
- XSS protection via proper input handling
- LDAPS (LDAP over SSL/TLS) required for ActiveDirectory
- HTTPS recommended for web frontend
- Credentials never logged or stored
Location: main.go:34-36
All responses compressed with Brotli:
app.Use(compress.New(compress.Config{
Level: compress.LevelBestSpeed,
}))Location: main.go:38-41
Static assets cached for 24 hours:
app.Use("/static", filesystem.New(filesystem.Config{
Root: http.FS(static.Static),
MaxAge: 24 * 60 * 60,
}))Location: main.go:29-32
Request size limited to prevent abuse:
app := fiber.New(fiber.Config{
BodyLimit: 4 * 1024, // 4KB
})# Successful password change
curl -X POST http://localhost:3000/api/rpc \
-H "Content-Type: application/json" \
-d '{
"method": "change-password",
"params": ["testuser", "OldPass123!", "NewPass456!"]
}'
# Expected response:
# {"success":true,"data":["password changed successfully"]}
# Validation error (password too short)
curl -X POST http://localhost:3000/api/rpc \
-H "Content-Type: application/json" \
-d '{
"method": "change-password",
"params": ["testuser", "OldPass123!", "short"]
}'
# Expected response:
# {"success":false,"data":["the new password must be at least 8 characters long"]}
# Method not found
curl -X POST http://localhost:3000/api/rpc \
-H "Content-Type: application/json" \
-d '{
"method": "invalid-method",
"params": []
}'
# Expected response:
# {"success":false,"data":["method not found"]}No integration tests currently implemented. See Testing Guide for recommendations.
- Architecture Patterns - Design decisions and patterns
- Development Guide - Setup and workflow
- Testing Guide - Testing strategies
- Component Reference - Detailed component documentation
Generated by /sc:index on 2025-10-04