Results 1 to 1 of 1
  1. #1
    Java Exam is offline Member
    Join Date
    Dec 2011
    Rep Power

    Default Tutorial:Review of Web Application Security for the Web Component Developer Exam II

    In this second article on web application security, we will look in depth at programmatic web application security. In the first article on the subject, “Tutorial:Review of Web Application Security for the Web Component Developer Exam” we discussed declarative web security for you web applications. In this second article we will look at how to implement security programmatically. This is part of a series of articles to help you prepare for the web component developer exam.

    In the previous article, we summarized the key issues of web security. They are:
    • Authentication - the means which communicating entities (i.e. client and server) prove to one another that they are acting on behalf of specific identities that are authorized for access.
    • Authorization - the means by which interactions with resources are limited to sets of users or programs for the purpose of enforcing integrity, confidentiality, or availability constraints.
    • Confidentiality - the means used to ensure that information is made available only to users who are authorized to access it
    • Data Integrity - the means used to prove that information has not been modified by a third party while in transit
    • Non-Repudiation - the means used to prove that a user who performed some action cannot deny having done so. This is used to ensures that it can be proven that a particular transaction has taken place.

    We also said the all of web application security revolves around two key aspects:
    • Authorization and Authorization or being able to prevent unauthorized users from accessing sensitive resources
    • Encryption or being able to prevent attackers from stealing network data while it is in transit.

    These aspects are independent of each other for the most part. How you choose to authenticate and authorize access to resources is independent of whether we are using Transport Layer Security (TLS). Only if we are using client certificates do the approaches to authentication and authorization overlap with encryption.

    Basic Steps To Web Application Security

    Before we delve into programmatic security, let’s make sure that you are clear as to the interaction between a web application and web client transpires.

    Client Request for Restricted Resource
    The web client requests a restricted resource in the application. The resource is provided by the URL. As the client has not been authenticated within the application environment, the web server responsible for delivering the web portion of the application detects this request for a restricted resource and invokes the designated authentication mechanism for this resource. This is shown in the figure below:
    Tutorial:Review of Web Application Security for the Web Component Developer Exam II-a18-clientrequest.jpg
    Figure: The Client Request

    Initial Authentication
    The web container returns a form that the web client uses to gather authentication information (i.e. user name and password) from the user. The client submits the authentication information to the web server, where it is validated by the web server, as shown in the figure below. The validation mechanism may be local to a server or may leverage the company’s enterprise security services. On the basis of the validation, the web server sets a credential for the user.
    Tutorial:Review of Web Application Security for the Web Component Developer Exam II-a18-initialauthentication.jpg
    Figure: Initial Authentication

    Authorisation of Access to Restricted Resource
    The credential for the user can new be used to determination whether the user is authorized to access other restricted resources it may request. The web server checks the security policy associated with the set of restricted web resources to determine the security roles that are permitted access to the resource. The security policy is derived from either annotations or from the deployment descriptor. The web container tests the user’s credential against each role to determine whether it can map the user to that role. The web server’s evaluation stops when either an “is authorized” outcome or a “not authorized” outcome is determined. If the outcome “is authorized” the web server will map the user to a role. If the outcome is “not authorized” the web server will not map the user the to role. The figure below outlines this process:
    Tutorial:Review of Web Application Security for the Web Component Developer Exam II-a18-urlauthentication.jpg
    Figure: URL Authorization

    Access to the Restricted Resource
    If the user is authorized, the web server will then return the result of the original URL request, When the response URL of a web page is returned, the user can then enter data and post form data that needs to be handled by the business-logic component of the application. This is shown below:
    Tutorial:Review of Web Application Security for the Web Component Developer Exam II-a18-accessrestrictresource.jpg
    Figure: Access to Restricted Resource

    Invoking Java Bean or Servlet Business Method
    In the figure below, we have dived down to show parts of the Model View Controller pattern that is used in almost all web applications. Here the web page makes another request to the servlet controller or resource that has the user’s credential to establish a secure association between the web page and the model bean. All of this takes place within the security context established on the web server.
    Tutorial:Review of Web Application Security for the Web Component Developer Exam II-a18-businessmethodinvocation.jpg
    Figure: Business Method Invocation

    Hybrid Security Model with Programmatic Security

    Declarative security is very useful and provides an approach which facilitates the implementation of security on an existing web application. The approach is a coarse grain mechanism for controlling access to resources. There are only two possibilities for every resource; allowed or denied. There are no means of allowing resources to customize their output depending on the user or the role of the client.

    Programmatic security offers the possibility of fine grain control over the application’s resources. This means that you will be able to provide customizations with your resources that are just not possible with declarative security while still offering the convenience of container managed declarative security for authentication and authorization to complement the fine grain control over servlets and JSP pages. This hybrid security model is supported by the servlet specification which provides the following three methods in HttpServletRequest:
    • isUserInRole - determines if the currently authenticated user belongs to a specified role. If the user is not assigned that role, the method returns false. If no user is currently authenticated, the method returns false. The information about roles is kept in the standard password file as well as by defining roles using the security-role-ref element to create alias for standard roles. If no <security-role-ref> element is declared in a deployment descriptor, and the isUserInRole method is called, the container will default to checking the provided role name against a list of all security roles defined for the web application. If you use the default method rather than define the <security-role-ref> element, this limits your ability to modify role names in an application without a recompilation of the servlet making the call. An example of isUserInRole method is: request.isUserInRole("editor")
    • getRemoteUser - returns the name of the current authenticated user. The value return is a string with the username. If there is no currently authenticated user, the getRemoteUser will return null.
    • getUserPrincipal - returns a object with the current username wrapped inside. The use of the Principal object is to provide compatibility with preexisting security code from Java platform 1.1. If there is no currently authenticated user, getUserPrincipal returns null.

    This hybrid approach to mixing programmatic security with container-managed security. allows you to set up usernames, passwords, and roles using the server’s built-in mechanisms. You can still setup form-based or BASIC authentication using the login-config element on the server. As well you continue to setup form-based authentication with the same action (i.e. j_security_check) on the form as well as the same definition for the text fields using j_username and j_password. The difference now is that you can also add code to a subset of your resources to customize their behavior based on the user or the role.

    Using Aliases via Security Role References

    There are cases when you will look to reuse your servlets for other applications. Often the security configuration of the other application will be different than the configuration for the application which has the servlets that have been identified for reuse. In this situation, the security-role-ref subelement of servlet is very useful because it allows you to define servlet-specific synonyms (alias) for existing role names. The security-role-ref element has three possible subelements. These are:
    • description - optional descriptive text
    • role-name - the new synonym
    • role-link - the existing security role

    For example, there is functionality in a servlet from an web application called Match in which a user needs to have a role of coach. The new application called Training has different complementary functionality to that found in match. But in order to further enhance Training, it has been decided to reuse the match statistics servlet from the Match web application. The user, guyroux is setup in the Training application with a role of manager. Since there is no role of manager in the Match application, the way to address this is to user the security-role-ref to provide coach as an alias for manager.

    Also you have a servlet of class SeasonData that provides one type of information to a physiotherapist and another type to a sports psychologist. There is scope to reuse this servlet with the Tomcat password file that assigns users to the physiotherapist and psychologist roles.You can accomplish this by using the security-role-ref to say that isUserInRole("physiotherapist") should return true for the same users that isUserInRole("physicalTrainer") already would. Similarly, you can use security-role-ref to say that isUserInRole("psychologist") should return true for the same users that isUserInRole("motivators") would. Below is an example of the deployment descriptor for doing this:

    XML Code: Use of security-role-ref
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <web-app xmlns="" 
    	      xsi:schemaLocation= "
 " version="2.4">
          <role-name>manager</role-name> <!-- New alias. -->
          <role-link>coach</role-link> <!-- Preexisting role. --> 
       <servlet-name> SeasonData</servlet-name>
          <role-name> physiotherapist</role-name>	<!-- New. -->
          <role-link>physicalTrainer</role-link> <!-- Preexisting. --> 
          <role-name>psychologist</role-name>	<!-- New. -->
          <role-link>motivators</role-link> <!-- Preexisting. --> 
    Programmatically Managing All Web Security

    In most situations, declarative security will be sufficient to meet the security needs for a web application. Container managed authentication is done in a manner that is transparent for all resources. Irrespective of whether you use form-base authentication, BASIC authentication or encrypted traffic, there is no impact or changes that are required to the resources of your application. The disadvantage of this approach is that configuring the security architecture requires a server specific component to setup username, passwords and user roles. But this is generally outweighed by the convenience and speed in which security can be applied to an application.

    In cases, where you would like to have fine-grain control over the security of the application’s resources or for the servlets or JSP pages to be self-contained with no server dependencies or entries. Going down this road, will require a lot of work to achieve this. The implication though it that once completed, the resources can be ported with relative ease from one server to another without the requirement of configuring the container managed security. By using programmatic security, you can even bundle up a WAR file and forward it to someone and they can use it immediately with little or no configuration required on the server. You cannot due this using declarative security as the WAR file is tied to the container via the deployment file for server specific configuration. Also using programmatic security, you leverage other schemes for username and passwords rather than using a preconfigured list. For example you could store usernames and roles in the HttpSession (if cookies are not disabled) or you could use a filter to intercept all requests for an application’s resources and check for user information in the HttpSession. This information would then be used to permit access to the requested resource. If the user has not been authenticated, they can be forwarded to login page. The login page action would be used to authenticate the user. If authenticated, the user’s information would be saved into the HttpSession object. This approach allow you to have a login page that is aligned with the web standards of your application and also achieve the portability desired from the use of programmatic security. The disadvantage of it is the amount of work to create the initial security module. But as with any module, your team could develop a framework or template that could facilitate reuse of programmatic security across several applications allowing them to be portable across your company’s IT landscape.

    HTTP Authentication using Programmatic Security

    HTTP supports two types of authentication. These are BASIC and DIGEST authentication. As very few browsers support DIGEST authentication, we will focus our review on BASIC authentication. If you have reviewed the previous article, “Tutorial:Review of Web Application Security for the Web Component Developer Exam”, you will know that BASIC authentication leverages a standard dialog box within the browser to provide a login page.
    Below are the steps involved in BASIC authentication using programmatic security:
    1. Check if there is an Authorization request header. If there is no such header, jump to Step 5.
    2. Get the encoded username/password string. The username and password is represented in base64 encoding. The Authorization header should have the following form: 

      XML Code: Authorization Header
      Authorization: Basic encodedData
    3. Decode the username/password string. You must use the BASE64Decoder class’ decodeBuffer method. It will return a string in the form username:password. The BASE64Decoder class is bundled with the JDK in the sun.misc package. Look for the rt.jar file within your JDK home directory. It is normally found at JDK_HOME/jre/lib/rt.jar.
    4. Validate the username and password. against your directory, database or file that stores usernames and passwords. For testing or for simple applications, you can also consider placing this information directly in the servlet. It’s important to protect the servlet’s class file and source code from outside access. Once the user is authenticated, you can return the requested page. Remember that here authentication is how you choose to implement it. You are not dependent on the container for doing this work. If they are not authenticated, please see the next step.
    5. If authentication has failed, your servlet will need to send back a response of 401 along with a header with the following form: 

      XML Code: Response Header
      WWW-Authenticate: BASIC realm="myAppRealm"

This response will prompt the browser to pop up a dialog box asking the user to enter a username and password for myAppRealm in order to reconnect with the username and password embedded within the base64 string inside the Authorization header.

    Base64 encoding is not intended to provide security since it is relatively trial to decode and read the original text. To prevent attackers from accessing network traffic between the client and the server, you will still need to implement SSL. This will encrypt the entire stream. The advantage of using SSL is that it is supported by most of the major commercial servers on the market and the servlets and will run without modification on these servers as on any other compliant with the servlet specification. The only change is the use of https rather than http as the prefix of the URL. Encryption and decryption on SSL Servers are handled transparently before the servlets are invoked.

    A final point about base64 encoding is that it was originally provided by Sun to decode strings that were encoded with base64. These classes are located in the sun.misc package hierarchy are therefore not part of the official language specification so you might not find them in all implementations of the Java Development Kit. You will need to explicitly include the class file when you distribute your application. An easy way to do this is to make the JAR file available to all Web applications on your server while explicitly recording that your applications depend on the base64 encoding class.

    Below we provide an example of programmatic security used for authentication. In the servlet we prompt the user to log in and displays information about the current user. We also output the information provided in the login attempt. Finally the user can also logout and we output the information related to what happens during a logout. The listing for the servlet is provided below.
    Java Code: Example of Programmatic Security
    @WebServlet(name = "SecurityServlet", urlPatterns = { "/SecurityServlet" })
    public class SecurityServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	protected void doGet(HttpServletRequest request,
    			HttpServletResponse response) throws ServletException, IOException {
    		PrintWriter out = response.getWriter();
    		try {
    			String userName = request.getParameter("txtUserName");
    			String password = request.getParameter("txtPassword");
    			out.println("Before Login" + "<br><br>");
    			out.println("IsUserInRole?.." + request.isUserInRole("adminuser")
    					+ "<br>");
    			out.println("getRemoteUser?.." + request.getRemoteUser() + "<br>");
    			out.println("getUserPrincipal?.." + request.getUserPrincipal()
    					+ "<br>");
    			out.println("getAuthType?.." + request.getAuthType() + "<br><br>");
    			try {
    				request.login(userName, password);
    			} catch (ServletException ex) {
    				out.println("Login Failed with a ServletException.."
    						+ ex.getMessage());
    			out.println("After Login..." + "<br><br>");
    			out.println("IsUserInRole?.." + request.isUserInRole("adminuser")
    					+ "<br>");
    			out.println("getRemoteUser?.." + request.getRemoteUser() + "<br>");
    			out.println("getUserPrincipal?.." + request.getUserPrincipal()
    					+ "<br>");
    			out.println("getAuthType?.." + request.getAuthType() + "<br><br>");
    			out.println("After Logout..." + "<br><br>");
    			out.println("IsUserInRole?.." + request.isUserInRole("adminuser")
    					+ "<br>");
    			out.println("getRemoteUser?.." + request.getRemoteUser() + "<br>");
    			out.println("getUserPrincipal?.." + request.getUserPrincipal()
    					+ "<br>");
    			out.println("getAuthType?.." + request.getAuthType() + "<br>");
    		} finally {
    Implementing Basic Authentication Using Annotations

    Rather than needing to go through the process of configuring basic authentication in the deployment descriptor and security file, you can specify the security of your servlet using the @DeclareRoles annotation. The @DeclareRoles annotation can be used in place of the deployment descriptor to define roles that you can test via the isUserInRole method.
    As you may remember, security roles are logical grouping of users classified by a set of common characteristics within the context of an application. It could be in terms of the title that the individual retains within a company or in terms of an individual’s customer profile. In a deployed application, the roles are linked to security identities or previously known as principles (i.e. that allows for the user in the role to have access to restricted sets of resources within the application.

    In terms of the value passed to the isUserInRole method, the String argument of the isUserInRole represents the role of the authenticated user. The security role reference defines the mapping between the name of the security role that has been defined in the application and the name of the security role being provided as an argument from a call to isUserInRole(String role) in a web component.

    The preferred method of declaring roles referenced in your application is to use the @DeclareRoles annotation. The @DeclareRoles annotation allows for your developer to have more control over the definition of roles within the application. Below we provide an example of the use of @DeclareRoles within a servlet for football training. The listing is shown below:
    Java Code: Example of use of @DeclareRoles
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    @DeclareRoles({"manager", "player"})
    public class FootballServlet extends HttpServlet {
        public void service(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            PrintWriter out = resp.getWriter();
            out.println("<HTML><HEAD><TITLE>Football Training</TITLE>
             if (req.isUserInRole("manager") && !req.isUserInRole("player")) {
                out.println("Let’s Train");
            } else {
                out.println("Invalid roles");
    In this example, we have specified only authenticated users with role of either manager or player can access this servlet. By using the @DeclareRoles annotations, you can avoid the need to setup roles in the deployment descriptor except for anything other than custom authentication mechanisms that are tied to the server environment hosting your servlets.

    Using the @RunAs Annotation
    If your web application is hosted in a Java EE compliant container, you can also use the
    @RunAs annotation that defines the role of the application during execution in a Java EE container. You can specify it on a class to allow developers to execute an application under a particular role. The role must map to the user/group information in the web container’s security realm. The security role in the application must match the value element in the annotation during it’s execution in a Java EE container. This corresponds with the run-as element that can be defined for a servlet in the deployment descriptor. Below is an example of the use of @RunAs:

    Java Code: Example of use of @RunAs annotation
    public class SpeedTrialServlet {
        private Runner runner;
        public void doGet(HttpServletRequest, req, HttpServletResponse res) {
    Using Programmatic Security with SSL

    Following on from our discussion on implementing authentication using programmatic security, from the previous section, you should be aware that SSL can be used with servlet managed security similar to container-managed security. This approach, in a similar vein to all programmatic security will allow for more portability of the implementation at the expense of an increase initial workload. Again you can do this in a way that provides a framework or template for your team to use on other applications going forward. It depends on your circumstance. There are certain capabilities that are required to use SSL in programmatic security that are not necessarily needed for programmatic security without SSL.

    Determining whether SSL Is in Use

    You can use the ServletRequest’s getScheme method to determine whether SSL is being used. If it returns a string with “https”, then it is using SSL, if it returns “http” it is not. You can also use another method of ServletRequests. That is the isSecure method. It returns true for SSL requests.

    Redirecting Non-SSL Requests

    When you receive a normal request in your servlet, you will need to explicitly redirect it into a secure request. This is different from declarative container-managed security where you use the transport-guarantee subelement of the user-data-constraint to ensure that the server takes regular http requests and redirects them into the https (SSL) requests. The key is to generate the URL, once you have it, the redirection process is very easy. You only need to call response.sendRedirect.

    The difficulty is in generating the URL in the first place. As there is no method in either ServletRequest or HttpServletRequest that will provide you with the complete requested URL, you will need to call request.getRequestURL to get the main URL, and then change it manually from http to https. Once you have made your modifications, you then add any form data using request.getQueryString and then pass the final result to response.sendRedirect.

    Unfortunately this approach leads to several problems of portability. In the instructions above we have assumed that SSL is running on the default port 443. If it is running on another port, then this approach will produce errors since we are redirecting to the wrong port. This implies that you will need to know about the port configuration of the environment where the server is hosted. In general this is a minor inconvenience and doesn’t impact strongly on the portability of this solution since most servers continue to use the default port for SSL.

    Discovering the Number of Bits in the Key

    Although it is all well and good that you have encrypted access to your company’s sensitive resources and that these resources are only available to authorized users but if you failure to determine the level of encryption of your network traffic, your security could all be for nought. It is not difficult for a semi-skilled hacker to decrypt network traffic that has a weak level of encryption. To avoid this scenario, you will need to discover the level of encryption being used. Fortunately since version 2.3 of the servlet API, SSL requests automatically result in an attribute named javax.servlet.request.key_size being placed in the request object. You can then use the method request.getAttribute with the specified key_size name to access this value. The return is of type Integer and this will tell you the length of the encryption key. You will need to cast the return to Integer since all values returned by getAttribute are of return type Object. If the result is null, this means that it is a non-SSL request. An example of this is shown below:
    Java Code: Use of Encryption Key Attribute
     String keyAttribute = "javax.servlet.request.key_size"; 
    Integer keySize = (Integer)request.getAttribute(keyAttribute); 
    if (keySize != null) 
    { //process the length of the encryption key}
    Looking Up the Encryption Algorithm

    Another new feature added in version 2.3 of the servlet API was an attribute javax.servlet.request.cipher_suite that is also added to the request object when SSL requests are made. This is a String value that describes the encryption algorithm being used. Remember to cast the Object return type of getAttribute to String. Again if the result is null, then it is a non-SSL request. Below is an example of how to access it:
    Java Code: Use of Encryption Attribute
    String cipherAttribute = "javax.servlet.request.cipher_suite"; 
    String cipherSuite = (String)request.getAttribute(cipherAttribute); 
    if (cipherSuite != null) 
    {//process the encryption algorithm of the SSL Request}
    Accessing Client X.509 Certificates
    Many companies will use browsers that permit users to authenticate themselves using X.509 certificates. If the client uses an X.509 certificate for authentication the certificate can be access by using the javax.servlet.request.X509Certificate attribute in the request object. The object returned by getAttribute can be cast from Object into an object of type This object contains full and complete information about the certificate. If the result is null, this means that it is either a non-SSL requests or a SSL requests without a certificate. An example of how to access this object is shown below:
    Java Code: Use of X509 Certificate Attribute
    String certAttribute = "javax.servlet.request.X509Certificate"; 
    X509Certificate certificate = (X509Certificate)request.getAttribute(certAttribute); 
    if (certificate != null) 
    { // process the certificate and interrogate for information }
    You can use the X.509 certificate, to look up the issuer’s distinguished name, the serial number, the raw signature value, the public key, and a number of other pieces of information. For more information about X.509 Certificates, go to X.509 certificates

    Below is an example of a servlet that redirects non-SSL requests to SSL Requests. This server does two key things. If it receives a non-SSL request, it transforms it into an SSL request. For an SSL Request, it presents a page displaying key information about the request especially information that we spoke about as being unique to a SSL request (i.e. key size, encryption algorithm, and client certificate. Note that what we do in this servlet is not recommended. In a production system you should redirect users when they access a servlet or JSP page that has a form for collecting data. You cannot do this after the user has submitted the data. At this point your data could possibly already be compromised. The listing is shown below:
    Java Code: Eexample of Servlet doing SSL Redirect
    public class SSLSecurity extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	protected void doGet(HttpServletRequest request,
    			HttpServletResponse response) throws ServletException, IOException {
    		// Redirect non-SSL requests to the SSL equivalent.
    		if (request.getScheme().equalsIgnoreCase("http")) {
    			String origURL = request.getRequestURL().toString();
    			String newURL = httpsURL(origURL);
    			String formData = request.getQueryString();
    			if (formData != null) {
    				newURL = newURL + "?" + formData;
    		} else {
    			String currentURL = request.getRequestURL().toString();
    			String formData = request.getQueryString();
    			PrintWriter out = response.getWriter();
    			String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 "
    					+ "Transitional//EN\">\n";
    			String title = "Security Info";
    			out.println(docType + "<HTML>\n" + "<HEAD><TITLE>" + title
    					+ "</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n"
    					+ "<H1>" + title + "</H1>\n" + "<UL>\n" + "  <LI>URL: "
    					+ currentURL + "\n" + "  <LI>Data: " + formData);
    			boolean isSecure = request.isSecure();
    			if (isSecure) {
    			   String keyAttribute = "javax.servlet.request.key_size";
    			   // Available only with servlets 2.3
    			   Integer keySize = (Integer) request.getAttribute(keyAttribute);
    			   String sizeString = replaceNull(keySize, "Unknown");
    			   String cipherAttribute = "javax.servlet.request.cipher_suite";
    			   // Available only with servlets 2.3
    			   String cipherSuite = (String) request
    			   String cipherString = replaceNull(cipherSuite, "Unknown");
    			   String certAttribute = 				"javax.servlet.request.X509Certificate";
    			   // Available with servlets 2.2 and 2.3
    			   X509Certificate certificate = (X509Certificate) request
    			   String certificateString = replaceNull(certificate, "None");
    				out.println("  <LI>SSL: true\n" + "  <UL>\n"
    					+ "    <LI>Key Size: " + sizeString + "\n"
    					+ "    <LI>Cipher Suite: " + cipherString + "\n"
    					+ "    <LI>Client Certificate: " + certificateString
    						+ "\n" + "  </UL>");
    			out.println("</UL>\n" + "</BODY></HTML>");
    	private String httpsURL(String origURL) {
    		int index = origURL.indexOf(":");
    		StringBuffer newURL = new StringBuffer(origURL);
    		newURL.insert(index, 's');
    		return (newURL.toString());
    OK. That’s it for web security. We have covered both programmatic and declarative security as well as the use of annotations for configuring the default setting within the servlet code. Next we dive deep into Java Server Pages. See you then.
    Last edited by Java Exam; 01-23-2012 at 05:54 PM.

Similar Threads

  1. Replies: 0
    Last Post: 01-20-2012, 08:09 PM
  2. Replies: 0
    Last Post: 01-09-2012, 08:25 PM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts