1   package com.atlassian.seraph.util;
2   
3   import org.apache.commons.codec.binary.Base64;
4   import org.apache.log4j.Category;
5   
6   import javax.servlet.ServletContext;
7   
8   import com.atlassian.seraph.config.SecurityConfig;
9   import com.atlassian.seraph.auth.Authenticator;
10  
11  public class SecurityUtils
12  {
13      private static final Category log = Category.getInstance(SecurityUtils.class);
14  
15      /** The Basic authorization encoding method. Includes trailing space, for convenient startsWith() checks, substring() and concatenation. */
16      private static final String BASIC_AUTHZ_TYPE_PREFIX = "Basic ";
17  
18      public static Authenticator getAuthenticator(ServletContext servletContext)
19      {
20          SecurityConfig securityConfig = (SecurityConfig) servletContext.getAttribute(SecurityConfig.STORAGE_KEY);
21  
22          if (securityConfig.getAuthenticator() == null)
23          {
24              log.error("ack! Authenticator is null!!!");
25          }
26  
27          return securityConfig.getAuthenticator();
28      }
29  
30      public static boolean isBasicAuthorizationHeader(String header)
31      {
32          return (header != null) && header.startsWith(BASIC_AUTHZ_TYPE_PREFIX);
33      }
34  
35      /**
36       * Extracts the username and password from the given header string (including the 'Basic ' prefix).
37       * @param basicAuthorizationHeader the header to decode.
38       * @return the credentials, or a username and password of "" if there were no credentials to decode.
39       */
40      public static UserPassCredentials decodeBasicAuthorizationCredentials(String basicAuthorizationHeader)
41      {
42          final String base64Token = basicAuthorizationHeader.substring(BASIC_AUTHZ_TYPE_PREFIX.length());
43          // note: header fields are not UTF-8, but ISO Latin 1, even when base 64 encoded
44          final String token = SecurityUtils.decodeBase64(base64Token, CharsetUtils.ISO_LATIN_1_CHARSET);
45  
46          String userName = "";
47          String password = "";
48  
49          final int delim = token.indexOf(":");
50  
51          if (delim != -1)
52          {
53              userName = token.substring(0, delim);
54              password = token.substring(delim + 1);
55          }
56          return new UserPassCredentials(userName, password);
57      }
58  
59      /**
60       * Reverses the operation of decodeBasicAuthorizationCredentials. Mainly for unit tests, or ServletFilters faking
61       * basic authorization.
62       * @param username the username to encode.
63       * @param password the password to encode.
64       * @return the encoded credentials.
65       */
66      public static String encodeBasicAuthorizationCredentials(String username, String password)
67      {
68          return BASIC_AUTHZ_TYPE_PREFIX + encodeBase64(username + ":" + password, CharsetUtils.ISO_LATIN_1_CHARSET);
69  
70      }
71  
72      /**
73       * Decodes the given Base64 encoded string using the given charset.
74       * @param encodedString the string to decode.
75       * @param charset the charset to decode it with.
76       * @return the decoded string.
77       */
78      private static String decodeBase64(String encodedString, String charset)
79      {
80          byte[] encodedBytes = CharsetUtils.bytesFromString(encodedString, CharsetUtils.US_ASCII_CHARSET);
81          byte[] decodedBytes = Base64.decodeBase64(encodedBytes);
82          return CharsetUtils.stringFromBytes(decodedBytes, charset);
83      }
84  
85      private static String encodeBase64(String decodedString, String charset)
86      {
87          byte[] decodedBytes = CharsetUtils.bytesFromString(decodedString, charset);
88          byte[] encodedBytes = Base64.encodeBase64(decodedBytes);
89          return CharsetUtils.stringFromBytes(encodedBytes, CharsetUtils.US_ASCII_CHARSET);
90      }
91  
92      /**
93       * User credentials including a username and a password.
94       * TODO Eliminate duplication between this class and {@link com.atlassian.seraph.filter.PasswordBasedLoginFilter.UserPasswordPair}
95       */
96      public static class UserPassCredentials
97      {
98          private final String username;
99          private final String password;
100 
101         public UserPassCredentials(String username, String password)
102         {
103             this.username = username;
104             this.password = password;
105         }
106 
107         public String getUsername()
108         {
109             return username;
110         }
111 
112         public String getPassword()
113         {
114             return password;
115         }
116     }
117 }