0

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
            }
Doreen
  • 714
  • 2
  • 14
  • 36
  • 1
    It's an Active Directory issue, Special Manager is in not in a group that permits him/her access. Review AD OU's & etc with a fine tooth comb – Jeremy Thompson Nov 01 '17 at 00:09
  • Thank you @Jeremy for pointing me in the right direction. Your insight convinced my NA that we needed to explore AD more. – Doreen Nov 01 '17 at 18:51

1 Answers1

0

My NA and I figured out the problem. The code used to retrieve the current user is System.Environment.UserName which retrieves the pre-2000 display name in AD properties that was shown as for example, "SManager" instead of "smanager". The comparison made to allow access to Page2 of app needed to be lowercase, thus in this case, SPmanager did not match smanager in the list of 3 reviewers to compare to.

The solution is to change the pre-2000 username in AD where UpperCase initials were once used to all lowercase which is done automatically now, in newer versions of AD.

I am a bit unimpressed that MSDN doesn't specify exactly, where it is retrieving the Username property.

Doreen
  • 714
  • 2
  • 14
  • 36