Skip to content

WritePhoto causes site-wide 500 on concurrent logins due to unhandled file lock exception #97

Description

@tonyuatkins-afk

When multiple users authenticate simultaneously (e.g. after an app domain restart), Utilities.WritePhoto throws an unhandled System.IO.IOException ("The process cannot access the file because it is being used by another process") that crashes the entire DNN request pipeline, taking the site down for all users.

The exception propagates all the way up through WindowsSignin.aspx, making the signin page inaccessible site-wide until the locked file is manually deleted and IIS is restarted. I experienced this today in production -- it was not fun!

To reproduce

  1. Have 20+ active users on the site
  2. Trigger an app domain restart (e.g. install any DNN module, which modifies web.config)
  3. All users re-authenticate simultaneously, hitting WritePhoto concurrently for the same user
  4. File lock race condition throws IOException inside WritePhoto
  5. Unhandled exception crashes WindowsSignin.aspx with HTTP 500 for all subsequent requests

Expected behavior

WritePhoto should handle IOException gracefully -- either with a try/catch that logs the error and skips the photo write, or with a file lock mechanism that prevents concurrent writes. A photo sync failure should never be fatal to the authentication pipeline.

Actual behavior

Site goes down completely. All users get HTTP 500. Recovery requires manually deleting the locked photo file from Portals\0\Users\{id}\ and restarting IIS.

Stack trace

System.IO.IOException: The process cannot access the file 
'C:\inetpub\wwwroot\DNN\Portals\0\Users\255\01\-1\employeephotofilename.jpg' 
because it is being used by another process.
   at DotNetNuke.Services.FileSystem.FileManager.AddFile(...)
   at DotNetNuke.Authentication.ActiveDirectory.ADSI.Utilities.WritePhoto(ADUserInfo, Byte[])
   at DotNetNuke.Authentication.ActiveDirectory.ADSI.ADSIProvider.FillUserInfo(...)
   at DotNetNuke.Authentication.ActiveDirectory.ADSI.ADSIProvider.GetUser(String)
   at DotNetNuke.Authentication.ActiveDirectory.AuthenticationController.AuthenticationLogon()

Environment

  • DNN version: 10.2.2
  • DNN.ActiveDirectory version: latest
  • Windows Server 2022 / IIS 10
  • .NET Framework 4.8

Workaround

Set SynchProfilePicture = False in the HostSettings table to disable photo sync entirely. This prevents the crash but loses photo sync functionality.

INSERT INTO dbo.HostSettings (SettingName, SettingValue, SettingIsSecure)
VALUES ('SynchProfilePicture', 'False', 0)

Suggested fix

The current code (inferred from stack trace) calls FileManager.AddFile with no exception handling. The fix is a two-part change.

Part 1: Wrap the entire WritePhoto body in a try/catch

Public Shared Sub WritePhoto(objUserInfo As ADUserInfo, photo() As Byte)
    Try
        ' ... existing photo write logic ...
        Dim folder = FolderManager.Instance.GetFolder(portalId, folderPath)
        Dim fileName = objUserInfo.Username & "_profile_photo.jpg"

        Using stream As New System.IO.MemoryStream(photo)
            FileManager.Instance.AddFile(folder, fileName, stream, True)
        End Using
    Catch ex As System.IO.IOException
        ' Another thread is writing this user's photo -- skip silently.
        ' This is benign: the photo will sync on the user's next login.
        DotNetNuke.Services.Exceptions.Exceptions.LogException(ex)
    Catch ex As Exception
        ' Any other photo sync failure must never crash the auth pipeline.
        DotNetNuke.Services.Exceptions.Exceptions.LogException(ex)
    End Try
End Sub

Part 2: Also wrap the WritePhoto call site in ADSIProvider.FillUserInfo

Try
    Utilities.WritePhoto(objUserInfo, photo)
Catch ex As Exception
    ' Photo sync failure is non-fatal -- log and continue authentication.
    DotNetNuke.Services.Exceptions.Exceptions.LogException(ex)
End Try

Why both: Part 1 fixes the root cause. Part 2 is defense-in-depth -- if any future code change to WritePhoto accidentally re-introduces an unhandled exception, the auth pipeline still won't crash.

The core principle: photo synchronization is a convenience feature. It must never be able to prevent users from logging in or bring down the site. Any exception from WritePhoto should be logged and swallowed, never propagated.

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions