A login audit plugin for Redmine 6.x and 7.0.
This is a refactored version of the original redmine_login_audit by Martin Denizet, updated for Redmine 6.x / 7.0.
- Web Login Logging: Records successful and failed login attempts
- API Access Logging: Tracks REST API authentication
- SAML/SSO Support: Captures failed logins for locked users via SSO (e.g., Okta, KeyCloak)
- Admin Interface: View logs with filtering, sorting, and CSV export
- Log Purging: Bulk deletion of old records with safety confirmation
- Redmine 6.0 or higher
cd /path/to/redmine/plugins
git clone https://github.com/seraph3000/redmine_login_audit2.git
cd /path/to/redmine
bundle exec rake redmine:plugins:migrate RAILS_ENV=productionRestart Redmine after installation.
To migrate from the original redmine_login_audit plugin while preserving existing data:
# 1. Remove old plugin (table remains)
cd /path/to/redmine
rm -rf plugins/redmine_login_audit
# 2. Install new plugin
cd plugins
git clone https://github.com/seraph3000/redmine_login_audit2.git
# 3. Check migration status
cd /path/to/redmine
bundle exec rake redmine_login_audit2:check RAILS_ENV=production
# 4. Run migration (preserves existing data)
bundle exec rake redmine_login_audit2:migrate_from_v1 RAILS_ENV=production
# 5. Restart Redmine
touch tmp/restart.txt
# 6. (Optional) Clean up old migration records
bundle exec rake redmine_login_audit2:cleanup_v1 RAILS_ENV=production| Task | Description |
|---|---|
redmine_login_audit2:check |
Check current migration status |
redmine_login_audit2:migrate_from_v1 |
Migrate from old plugin |
redmine_login_audit2:cleanup_v1 |
Remove old migration records |
redmine_login_audit2:stats |
Show statistics |
redmine_login_audit2:purge MONTHS=6 |
Delete old logs |
cd /path/to/redmine
bundle exec rake redmine:plugins:migrate NAME=redmine_login_audit2 VERSION=0 RAILS_ENV=production
rm -rf plugins/redmine_login_audit2Navigate to "Administration" → "Plugins" → "Redmine Login Audit 2" → "Configure":
| Setting | Description |
|---|---|
| Logging Setting | Nothing / Successes only / Failures only / Both |
| Audit API | Also log REST API access |
| Filter API Keys | Replace API keys in URL with [FILTERED] |
| Rows per Page | Number of entries per page in admin view |
Access logs via "Administration" → "Login Audit".
Filtering uses Redmine's standard filter UI. Add filters from the "Add filter" dropdown and combine them freely with operators (contains, is, starts with, date range, etc.):
- Login ID / IP Address / Method / URL (text operators)
- Success (yes / no)
- Source (Web / API)
- Created on (date operators: today, last N days, range, etc.)
The current filter state carries over to the statistics page.
- Delete logs older than specified months (displays oldest log date for reference)
- Delete all logs (requires checkbox confirmation to prevent accidental deletion)
- Email notification (unnecessary in SSO environments)
- wice_grid dependency (replaced with standard Redmine pagination)
alias_method_chain→prependpatternbefore_filter→before_actionattr_accessible→ Removed (Strong Parameters)unloadable→ Removed
- SAML/SSO Support: Properly logs failed authentication for locked/disabled users via SSO
- Streaming CSV Export: Memory-efficient export using batch processing (1,000 records at a time)
- Improved Purge UI: Displays oldest log date, collapsible section for cleaner interface
- Delete All Protection: Requires checkbox confirmation to prevent accidental deletion
- HTML5 Date Picker: Native date input fields (no calendar helper dependency)
This plugin is compatible with redmine_saml. When a user is locked in Redmine but authenticates via SSO, the failed login attempt is properly recorded.
| Method | Success | Failure | Notes |
|---|---|---|---|
| Redmine Standard Login | ✓ | ✓ | Built-in |
| LDAP / Active Directory | ✓ | ✓ | Uses standardtry_to_login |
| REST API (key/basic auth) | ✓ | ✓ | Built-in |
| SAML (redmine_saml) | ✓ | ✓ | Built-in |
| Other OmniAuth plugins | ✓ | — | Success via hook; failure requires extension |
Success logging works automatically for all authentication methods via Redmine's controller_account_success_authentication_after hook.
Failure logging for OmniAuth-based plugins (OAuth2, OpenID Connect, etc.) requires explicit support because each plugin handles callbacks differently.
If you use an OmniAuth-based authentication plugin not listed above, you can add failure logging support by modifying lib/redmine_login_audit2/account_controller_patch.rb.
Check your authentication plugin's source code for the callback method name. Common patterns:
| Plugin | Callback Method |
|---|---|
| redmine_saml | login_with_saml_callback |
| redmine_omniauth_google | oauth_google_callback |
| redmine_omniauth_github | oauth_github_callback |
| redmine_omniauth_azure | oauth_azure_callback |
| redmine_openid_connect | oic_callback |
Add a method to account_controller_patch.rb following this pattern:
# Example: Adding support for redmine_omniauth_google
def oauth_google_callback
auth = request.env['omniauth.auth']
if auth.present?
user = User.find_or_create_from_omniauth(auth)
if user.blank?
# User not found or could not be created
LoginAudit.failure(nil, request, { username: auth[:uid] || auth.info&.email })
elsif !user.active?
# User exists but is locked/disabled
Rails.logger.info "LoginAudit: User '#{user.login}' is not active (status=#{user.status})"
LoginAudit.failure(user, request, { username: user.login })
end
# Success is recorded automatically via the hook
end
super
endFor cleaner code, add a detection method to lib/redmine_login_audit2.rb:
def google_oauth_plugin_installed?
Redmine::Plugin.installed?(:redmine_omniauth_google)
rescue
false
endThen wrap your callback:
def oauth_google_callback
unless RedmineLoginAudit2.google_oauth_plugin_installed?
return super if defined?(super)
return render_404
end
# ... rest of the code
super
end- Always call
superat the end to let the original plugin handle the actual authentication - Check
user.active?— SSO can return locked users who pass external auth but should fail in Redmine - Use
auth[:uid]orauth.info.emailfor username when user is nil - Don't record success — the hook handles it automatically for active users
If failure logging doesn't work:
- Check the callback method name — Use
grep -r "def.*callback" plugins/redmine_omniauth_*to find it - Check if
find_or_create_from_omniauthexists — Some plugins use different methods likefind_by_provider_and_uid - Enable debug logging — Add
Rails.logger.debugstatements to trace execution - Verify plugin load order — This plugin should load after the auth plugin (alphabetical order usually works)
- Migrated filtering to Redmine core Query API: removed the custom filter logic in favor of the standard filter UI (operators, add/remove filters, multi-value)
- Redmine 7.0 support; migrated menu and legend icons to SVG sprites (
sprite_icon), with fallback for 6.x - Added statistics page: success/failure and API/Web ratios, daily trend, and top failure IPs/logins
- Added HTTP Referer recording per login event
- Fixed per_page not being respected on initial load
- Added filter labels (source, date range) across all bundled locales
- Initial release (refactored from redmine_login_audit)
- Redmine 6.x / Rails 7.2 support
- Web login and API access logging
- SAML/SSO failure logging (redmine_saml compatible)
- Streaming CSV export with batch processing
- Improved purge UI with oldest log date display
- Delete all protection with checkbox confirmation
- HTML5 native date picker for filters
GNU General Public License v2.0
- Original Author: Martin Denizet
- Refactored by: seraph3000