I have a C#.net 3.5 WAP solution with 4 projects. The web app allows access to only managers and HR. Authorization is granted in web.config by AD role. The problem is all managers can access Default and Page2 of the app with the exception of one manager who should also be able to access Page2 but can not (I'll call him specialmanager)(but can access Default). specialmanager is redirected to a custom Access Denied page when trying to access Page2 via URL with a query string or via select command link from gridview row on the default page.
Web.config:
<authorization>
<allow roles="abc\hr, abc\managers, abc\it, abc\software tester"/>
<allow users="abc\specialmanager"/>
<!--<deny users="*"/>-->
<!--<allow users="*"/>--><!--default-->
</authorization>
<authentication mode="Windows"/>
<identity impersonate="true"/>
<customErrors mode="Off" defaultRedirect="~/Warning/AccessDenied.aspx">
<error statusCode="401" redirect="~/Warning/AccessDenied.aspx" />
</customErrors>
I tried to log history of events in the method that is doing the redirect but when I place the error logging outside of a try catch an HTTP 401.2 error ensues. I’ve checked SQL Security for this one user and it’s correct – it’s the same as the others who have access. I’ve verified this user is a member of the AD group being granted access. I even allowed this particular user additional access in web config as a user in addition to a role and the same redirect to custom access denied page results. I have impersonated this user on my local debugging machine and all works well for me but not for him on his machine when accessing from the server so I can’t truly replicate what is going on as him. I have the username identified on the UI of every page and he is correctly identified. I am unsure how to debug this to find out why the app thinks he isn’t authorized. His username must match 1 of 3 reviewers listed in the row of grid on Default if he accesses it that way, and it does.
I only posted code that may be pertinent. I need help with ideas on how to debug this - any ideas?
Code to get username:
//returns username portion of AD domain\username
public String GetUserName()
{
string curUser = Environment.UserName; //returns the logged-in account-name
//// impersonate a specific user during testing & development+
//if (curUser == "user1" || curUser == "user2")
//{
// curUser = "specialuser"; //local dev. Test as user specified here.
//}
//else
//{
// return curUser;
//}
return curUser;
}
//check current user's membership in AD groups that are allowed to view form
//ref: http://stackoverflow.com/questions/12029378/how-to-check-if-a-user-belongs-to-an-ad-group
public List<Boolean> CheckGroupMembership()
{
List<bool> blnIsMember = new List<bool>();
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "abc");
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, GetUserName());
for (int i = 0; i <= 3; i++)
{
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, _groupName[i]);
if (user != null)
{
// check if user is member of that group
if (user.IsMemberOf(group))
{
_IsMember[i] = true;
blnIsMember.Add(_IsMember[i]);//add array to list
}
else
{
_IsMember[i] = false;
blnIsMember.Add(_IsMember[i]);
}
}
}
return blnIsMember;
}
Page2, Page_Load()
protected void Page_Load(object sender, EventArgs e)
{
lblProgressStatus.Text = "";
//Handle security depending on user's point of entry; 1) from app's default page or 2)Page2, URL/clicking on link in email notificiation
//call I_CurrentUser interface
I_CurrentUser user = new ReviewData();
//get current username
strCurrentUser = user.GetUserName();
HttpContext.Current.Session["UserName"] = strCurrentUser;
//check current user's membership against AD groups needed to gain access
_IsMember = user.CheckGroupMembership();
//pass list to array
IsMember = _IsMember.ToArray(); //0=Admin, 1=Manager, 2=Developer, 3=Software Tester
//Hide Admin link if current user is NOT a member of admin[0], developer[2] or software tester[3] group
if (IsMember[0] == true || IsMember[2] == true || IsMember[3] == true)
{
//Link to Admin page
LinkButton3.Visible = true;
}
else
{
//Link to Admin page
LinkButton3.Visible = false;
}
//prepare to log any error messages
DataLib.ErrorLog oErrorLog = new DataLib.ErrorLog();
if (!IsPostBack)
{
try
{
if (!string.IsNullOrEmpty(Request.QueryString["ID"]))
{
string id = Request.QueryString["ID"];
intID = Convert.ToInt16(id);
string strID = intID.ToString();
ViewState["ID"] = strID;
Session["ID"] = strID;
AuthorizeCurrentUserSelection(intID, strCurrentUser);
/* If DocID is not in querystring, the user has accessed form via URL. Determine if DocID exists:
* if it doesn't, Insert prelim record, if it does, update formname.
* Else DocID is in querystring.*/
if (string.IsNullOrEmpty(Request.QueryString["DocID"]))
{
DataLib.EPRConn oGetID = new DataLib.EPRConn();
intDocID = oGetID.SelectDocID(intID); //if docid does not exist it will return integer 0
string strDocID = intDocID.ToString();
ViewState["SelectedDocID"] = strDocID;
Session["SelectedDocID"] = strDocID;
//Insert DocID when it's null or empty, or "0"
if (string.IsNullOrEmpty(strDocID) || strDocID == "0")
{
/*4/24/17 IMPORTANT: InsertDocID() executes an initial INSERT DocID into tbl1 and tbl2,
this allows Ratings to be UPDATED rather than INSERTED when radio buttons are
subsequently ticked and establishes DocID and FormName prior to first submit/save.*/
DataLib.EPRConn oInsertID = new DataLib.EPRConn();
oInsertID.InsertDocID(Convert.ToInt32(ViewState["ID"]), intDocID);
}
else //Update FormName if DocID exists.
{
FillCommentTextBoxes(strID);
FillMeritIncreaseTextBox();
//Update tbl1.FormName and tbl2 where FormName IS NULL (to assign names to older forms created prior to this app's launch
DataLib.EPRConn oFN = new DataLib.EPRConn();
oFN.UpdateFormName(intID);
}
}
else //DocID exists in querystring.
{
string dID = Request.QueryString["DocID"];
intDocID = Convert.ToInt16(dID);
string strDocID = intDocID.ToString();
ViewState["SelectedDocID"] = strDocID;
Session["SelectedDocID"] = strDocID;
FillCommentTextBoxes(strID);
FillMeritIncreaseTextBox();
//Update FormName in 2 tables where FormName IS NULL (to assign names to older forms created prior to this app's
DataLib.EPRConn oFN = new DataLib.EPRConn();
oFN.UpdateFormName(intID);
}
Code that does the redirect to Page2:
//pass form ID and viewing supervisor/current user
protected void AuthorizeCurrentUserSelection(int ID, string supUserName)
{
//Create a list of strings to hold up to 3 reviewers of selected employee
List<string> strLine = new List<string>();
//prepare to log any errors thrown
DataLib.ErrorLog oErrorLog = new DatLib.ErrorLog();
try
{
//return all rows from tbl
DataLib.EPRConn oAuth = new DataLib.EPRConn();
strLine = oAuth.SelectSupervisorUserName(eID, supUserName);
string[] arr = strLine.ToArray();
string strLevel1 = arr[0];
string strLevel2 = arr[1];
string strLevel3 = arr[2];
//0=Admin, 1=Manager, 2=Developer, 3=Software Tester.
if (IsMember[0] == true || IsMember[2] == true || IsMember[3] == true {
BindRepeater();
}
//if current user is a manager and named in level1, 2 or 3, load grid
else if ((strLevel1 == strCurrentUser && IsMember[1] == true) ||
(strLevel2 == strCurrentUser && IsMember[1] == true) ||
(strLevel3 == strCurrentUser && IsMember[1] == true))
{
//Example of error log that throws HTTP 401 error when uncommented
//oErrorLog.WriteErrorLog("(strCurrentUser=" + strCurrentUser + ") History Log: Line398-401_AuthorizeCurrentUserSelection(" + eID + ", supUserName=" + supUserName + "). Level1=" + strLevel1 + ", Level2=" + strLevel2 + ", Level3=" + strLevel3 + ", IsMember[1]=true ");
//hide Lock/Unlock buttons
Panel1.Visible = false;
//show grid
BindRepeater();
}
else
{
//redirect
Response.Redirect("~/Warning/AccessDenied.aspx");
}//end if/else
}//end try
catch (SqlException ex)
{
oErrorLog.WriteErrorLog("(" + strCurrentUser + ")SqlException in ReviewForm_AuthorizeCurrentUserSelection(): " + ex.ToString());
} //end catch
}