Back to Blog

Today started with a bug report and ended with a release. That is my favourite kind of day.

The work was all in DBA Dash WebView, the browser-based companion I build for the excellent DBA Dash SQL Server monitoring platform. Neither issue involved a flashy new dashboard. Both were about something more important: making the application behave properly outside my own lab.

The first bug hid in Active Directory group mapping. The second was not a code crash at all, but an upgrade procedure with too much room for an administrator to ruin their evening. Together they were a good reminder that production bugs often live between systems, not inside a single function.

The LDAP bug that passed authentication and still got authorization wrong

Issue #96 described an annoying situation: Active Directory login worked, but the configured group mapping did not. Credentials were accepted, yet the application could not reliably decide whether the user belonged to the required, operator, or administrator group.

That distinction matters. Authentication answers who are you? Authorization answers what are you allowed to do? Getting the first answer right does not make the second one optional.

The old implementation read the user’s memberOf attribute and extracted the common name from every returned distinguished name. That is the obvious approach, and it works nicely in a small, tidy test domain.

Real Active Directory is rarely small or tidy.

memberOf normally contains direct memberships. If a user is in DBA Team, and DBA Team is nested inside DBA Dash Admins, the application may only see the first group. The group that actually grants the role is one level further up the tree. Large group lists add another wrinkle: Active Directory can return multi-valued attributes in ranges, so a client that reads only the first response can silently miss memberships.

There was also a smaller but very practical problem. Administrators do not all enter groups in the same form. Some use the short name:

DBA Dash Admins

Others copy the full distinguished name:

CN=DBA Dash Admins,OU=Security Groups,DC=example,DC=org

Both are reasonable. Both should work.

Asking Active Directory the right question

The fix now searches from the domain root and asks Active Directory for every group that contains the user, directly or through nested membership. The important part is Microsoft’s LDAP matching rule 1.2.840.113556.1.4.1941, often called LDAP_MATCHING_RULE_IN_CHAIN:

(&(objectCategory=group)
  (member:1.2.840.113556.1.4.1941:=<user distinguished name>))

Instead of downloading a partial list and trying to rebuild the group tree in the application, the query lets the directory resolve the hierarchy it already owns.

The search is paged in batches of 500 groups. It starts at the domain root rather than the optional user search base, because users and security groups are often stored in different organizational units. Returned group names and distinguished names are de-duplicated and compared case-insensitively. Role mapping accepts either representation, with administrator taking precedence over operator and operator over viewer.

I also kept a defensive fallback. If a directory does not support the transitive matching rule, WebView falls back to memberOf and follows ranged attributes until the full result has been read. Distinguished names are parsed properly now, including escaped commas and UTF-8 hex escapes. A group called DBA, Europe should not turn into a group called DBA simply because a comma happens to be meaningful in LDAP syntax.

Finally, the service logs how many groups were resolved and which lookup method succeeded. That sounds mundane, but it is exactly the information I want when debugging someone else’s domain without seeing their domain.

The fix landed in commit 33d7ba9 with tests for the LDAP filter, ranged attributes, DN parsing, escaping, name/DN matching, and role precedence.

Shipping the fix

The group-resolution change is available in DBA Dash WebView v0.2.4.

Before publishing it, I ran the backend test suite, the frontend tests, and a production frontend build. All 22 .NET tests and all four frontend tests passed. The release ZIP is attached to the GitHub release, ready for IIS rather than hidden inside a source archive.

That solved the login problem. Then issue #97 pointed at the next sharp edge: how exactly should an existing IIS installation be updated?

“Just copy the new files” is not an upgrade guide

An application can be easy to install and still be surprisingly easy to damage during an update.

DBA Dash WebView is read-only against DBADashDB, so there are no application database migrations to coordinate. That makes rollback pleasantly simple. The dangerous part is local state inside the deployment directory:

  • appsettings.json contains the database connection, JWT secret, and local authentication settings.
  • web.config contains the IIS hosting configuration.
  • config/ad-config.json contains the Active Directory settings and protected bind password.
  • config/local-users.json contains local users and password hashes.
  • config/thresholds.json contains the dashboard thresholds people tuned for their environment.

Extracting a new ZIP over the site without thinking can overwrite configuration. Deleting the old directory first can remove local users and thresholds. Changing the IIS application-pool identity can make the protected AD bind password unreadable because ASP.NET Core Data Protection is tied to its keyring and identity.

None of this is exotic. It is simply the part of deployment documentation that becomes very interesting five seconds after something goes wrong.

A boring update is a successful update

The README now has a complete Updating an Existing IIS Deployment section. The safe path is straightforward:

  1. Download the ZIP attached to the GitHub release and extract it to a staging directory.
  2. Back up appsettings.json, web.config, and the entire config directory somewhere outside the web root.
  3. Stop the IIS application pool so loaded DLLs are no longer locked.
  4. Copy the new release files into the site directory.
  5. Restore configuration and runtime state, then compare the new default settings with the restored configuration.
  6. Reapply read/execute permission to the application and modify permission to config.
  7. Start the pool, call /api/health, and test local login, AD login, dashboards, users, mappings, and thresholds.

The same section now covers app_offline.htm, Data Protection, rollback, and the cases where iisreset is actually useful. For a normal application update it is not required. There is no reason to restart every IIS site on a server when stopping one application pool does the job.

Future deployment ZIPs also contain a version.txt file. Tagged builds write the release tag; branch builds write the source commit SHA. It is a tiny addition, but “which version is installed on this server?” should have a better answer than comparing DLL timestamps and hoping nobody copied the folder last Tuesday.

The upgrade documentation and version marker landed in commit 5c9eb4d.

The useful part of bug reports

Both reports came from people trying the project in an environment that does not look exactly like mine. That is where open-source software becomes real.

A nested AD group exposes an assumption a flat test directory never will. A request for upgrade documentation exposes state that is obvious to the developer and invisible to the administrator holding a release ZIP. Neither report asked for a grand redesign. Both made the project safer and less surprising.

So, thank you to the people who opened the issues and described what happened. Please keep doing that. A clear bug report is not noise; it is a map to the part of the software that still believes the world is simpler than it is.

Tonight, DBA Dash WebView understands Active Directory groups better, has a repeatable IIS update path, and leaves a version marker behind after deployment. No new chart, no animated widget, no dramatic rewrite. Just two fewer traps for the next person.

Honestly, that is a pretty good release.


DBA Dash WebView: GitHub · v0.2.4 · Issue #96 · Issue #97