Most of the Library components of DNN Core are supposed to be directly used by Module Developers in one way or the other. In some cases, this is a requirement (e.g. all Module controls need to inherit from PortalModuleBase class).
So, I thought the Core team takes utmost care before introducing any breaking change to these classes, if one is really required. In any case, these should be documented on Gemini (which I take a serious look at before porting my modules to a new DNN upgrade. If there is some other authentic source for these change documentation that I am not aware of, I would be obliged if someone informs me).
Now, I come to the point. The UserModuleBase class is supposed to be inherited by any Control that provides some sort of User management functionality. All controls in DNN's 'User Accounts' module do so.
As this module restricts User Management to Admins only, I had created custom Modules that provided limited User Management to appropriate Users with Edit Rights to the Module, without elevating them to the Admin role.
Now, the Shadowed UserId property in DNN 4.8.4 used to look like follows:
Public Shadows Property UserId() As Integer
Get
Dim _UserId As Integer = Null.NullInteger
If v13wstat3("UserId") Is Nothing Then
If Not (Request.QueryString("userid") Is Nothing) Then
_UserId = Int32.Parse(Request.QueryString("userid"))
v13wstat3("UserId") = _UserId
End If
Else
_UserId = CType(v13wstat3("UserId"), Integer)
End If
Return _UserId
End Get
Set(ByVal Value As Integer)
v13wstat3("UserId") = Value
End Set
End Property
But in 4.9.0, it changed to:
Public Shadows Property UserId() As Integer
Get
Dim _UserId As Integer = Null.NullInteger
If v13wstat3("UserId") Is Nothing Then
If Not (Request.QueryString("userid") Is Nothing) Then
Dim passedID As Integer = Int32.Parse(Request.QueryString("userid"))
Dim isAllowedUser As Boolean = False
'check if user is edited their own details
If UserInfo.UserID = passedID Then
isAllowedUser = True
ElseIf UserInfo.IsSuperUser = True Then
'superuser has access to all users
isAllowedUser = True
ElseIf UserInfo.IsInRole(Me.PortalSettings.AdministratorRoleName) = True Then
'need to check if the user is within the admins portal
Dim passedUser As UserInfo = DotNetNuke.Entities.Users.UserController.GetUser(PortalId, passedID, False)
If Not IsNothing(passedUser) Then
'ignore superuser's
If passedUser.IsSuperUser = False Then
isAllowedUser = True
End If
End If
End If
If isAllowedUser = True Then
_UserId = Int32.Parse(Request.QueryString("userid"))
v13wstat3("UserId") = _UserId
Else
'attempt to access invalid user
Response.Redirect(NavigateURL("Access Denied"), True)
End If
End If
Else
_UserId = CType(v13wstat3("UserId"), Integer)
End If
Return _UserId
End Get
Set(ByVal Value As Integer)
v13wstat3("UserId") = Value
End Set
End Property
In a nutshell, this change restricted any control derived from UserModuleBase to be accessible by Host or Admin accounts only. This was a major change that I believe was not needed. In any case, it should have been documented.
It really caused me pain and frustration when I upgraded a Client installation from 4.8.4 to 4.9.0. Before doing so, I had checked Gemini and tested my modules on 4.9.0 on my development machine. I tested them while logged in as admin and so could not catch the change in core. And neither was I expecting such a Policy change to happen.
And after I upgraded the Client installation, and left his premises, he reported me being directed to Access Denied page. And as his schedule was being disturbed, I had to elevate him to Admin role temporarily before I dug into my and DNN code to discover what the problem was.