source: VirtualCollectionRegistry/trunk/VirtualCollectionRegistry/src/main/java/eu/clarin/cmdi/virtualcollectionregistry/gui/auth/BasicAuthStrategy.java @ 5417

Last change on this file since 5417 was 5417, checked in by Oliver Schonefeld, 10 years ago
  • cleanup source code
    • whitespace cleanup
    • remove unused imports
    • add missing @Override annotations
    • cleanup some @SuppressWarnings?("serial") annotations
  • Property svn:eol-style set to native
File size: 10.7 KB
Line 
1package eu.clarin.cmdi.virtualcollectionregistry.gui.auth;
2
3import java.io.BufferedReader;
4import java.io.FileInputStream;
5import java.io.IOException;
6import java.io.InputStream;
7import java.io.InputStreamReader;
8import java.io.UnsupportedEncodingException;
9import java.util.HashMap;
10import java.util.Map;
11import java.util.concurrent.locks.Lock;
12import java.util.concurrent.locks.ReadWriteLock;
13import java.util.concurrent.locks.ReentrantReadWriteLock;
14import java.util.regex.Matcher;
15import java.util.regex.Pattern;
16
17import javax.servlet.FilterConfig;
18import javax.servlet.ServletException;
19import javax.servlet.http.HttpServletRequest;
20import javax.servlet.http.HttpServletResponse;
21
22import org.apache.commons.codec.binary.Base64;
23import org.apache.commons.codec.digest.DigestUtils;
24
25/**
26 *
27 * @deprecated to be replaced with basic authentication mechanism at container
28 * level (e.g. tomcat-users)
29 */
30@Deprecated
31final class BasicAuthStrategy implements AuthStrategy {
32    private enum HashMethod {
33        PLAIN, MD5, SHA;
34
35        public static HashMethod fromString(String s) {
36            if ("PLAIN".equalsIgnoreCase(s)) {
37                return HashMethod.PLAIN;
38            } else if ("MD5".equalsIgnoreCase(s)) {
39                return HashMethod.MD5;
40            } else if ("SHA".equalsIgnoreCase(s)) {
41                return HashMethod.SHA;
42            } else {
43                return null;
44            }
45        }
46    } // enum Hash
47
48    private static final class Entry {
49        private final HashMethod type;
50        private final String username;
51        private final String password;
52        private final Map<String, String> attributes;
53
54        public Entry(HashMethod type, String username, String password, Map<String, String> attributes) {
55            this.type = type;
56            this.username = username;
57            this.password = password;
58            this.attributes = attributes;
59        }
60
61        public boolean checkPassword(String pw) {
62            switch (type) {
63                case PLAIN:
64                    return this.password.equals(pw);
65                case MD5:
66                    final String hash1 =
67                        DigestUtils.md5Hex(prepare(pw)).toLowerCase();
68                    return this.password.equals(hash1);
69                case SHA:
70                    final String hash2 =
71                        DigestUtils.shaHex(prepare(pw)).toLowerCase();
72                    return this.password.equals(hash2);
73            } // switch
74            return false;
75        }
76
77        public Map<String, String> getAttributes() {
78            return attributes;
79        }
80
81        private String prepare(String pw) {
82            // FIXME: salt should include something random
83            return new StringBuilder(this.username)
84                .append(':')
85                .append(pw)
86                .toString();
87        }
88    } // class Entry
89
90    private static final String CONFIG_PARAM_USERDB_FILE =
91        "authfilter.basic.userdb";
92    private final ReadWriteLock userDbLock =
93        new ReentrantReadWriteLock(true);
94    private final Map<String, Entry> userDb =
95        new HashMap<String, Entry>();
96
97    @Override
98    public void init(FilterConfig filterConfig, Map<String, String> config)
99            throws ServletException {
100        String userdb = config.get(CONFIG_PARAM_USERDB_FILE);
101        if ((userdb == null) || userdb.isEmpty()) {
102            throw new ServletException("missing init parameter '" +
103                    CONFIG_PARAM_USERDB_FILE + "'");
104        }
105        try {
106            InputStream in = new FileInputStream(userdb);
107            loadUserDatabase(in);
108        } catch (IOException e) {
109            throw new ServletException("error initializing user database", e);
110        }
111    }
112
113    @Override
114    public String getAuthType() {
115        return HttpServletRequest.BASIC_AUTH;
116    }
117
118    @Override
119    public void requestAuth(HttpServletRequest request,
120            HttpServletResponse response) throws IOException, ServletException {
121      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
122      final String realm =
123          "Basic realm=\"CLARIN Virtual Collection Registry\"";
124      response.addHeader("WWW-Authenticate", realm);
125    }
126
127    @Override
128    public Result handleAuth(HttpServletRequest request,
129            HttpServletResponse response) throws IOException, ServletException {
130        String authorization = request.getHeader("Authorization");
131        if (authorization != null) {
132            authorization = authorization.trim();
133            if (authorization.isEmpty()) {
134                authorization = null;
135            }
136        }
137        Result result = new Result();
138        if (authorization != null) {
139            int pos = authorization.indexOf(' ');
140            if (pos != -1) {
141                final String scheme = authorization.substring(0, pos).trim();
142                if (scheme.equalsIgnoreCase("Basic")) {
143                    final String credentials =
144                        decodeBase64(authorization.substring(pos + 1).trim());
145                    pos = credentials.indexOf(':');
146                    if ((pos != -1) && (pos < credentials.length() - 2)) {
147                        final String username = credentials.substring(0, pos);
148                        final String password = credentials.substring(pos + 1);
149                        if (checkCredential(username, password)) {
150                            Map<String, String> attr = getAttributes(username);
151                            // login successful
152                            result.setPrinicpal(
153                                    new AuthPrincipal(username, attr));
154                            result.setAction(Action.CONTINUE_AUTHENTICATED);
155                        } else {
156                            // login failed, retry
157                            result.setAction(Action.RETRY);
158                        }
159                    } else {
160                        result.setAction(Action.ERROR);
161                    }
162                } else {
163                    // unsupported auth method, retry
164                    result.setAction(Action.RETRY);
165                }
166            } else {
167                result.setAction(Action.ERROR);
168            }
169        }
170        return result;
171    }
172
173    private String decodeBase64(String s) throws ServletException {
174        try {
175            byte[] bytes = Base64.decodeBase64(s);
176            return new String(bytes, "US-ASCII");
177        } catch (UnsupportedEncodingException e) {
178            throw new ServletException("unsupported encoding");
179        }
180    }
181
182    private boolean checkCredential(String username, String password) {
183        Entry entry = null;
184        Lock lock = userDbLock.readLock();
185        lock.lock();
186        try {
187            entry = userDb.get(username);
188        } finally {
189            lock.unlock();
190        }
191        if (entry != null) {
192            return entry.checkPassword(password);
193        }
194        return false;
195    }
196
197    private Map<String, String> getAttributes(String username) {
198        Entry entry = null;
199        Lock lock = userDbLock.readLock();
200        lock.lock();
201        try {
202            entry = userDb.get(username);
203        } finally {
204            lock.unlock();
205        }
206        if (entry != null) {
207            return entry.getAttributes();
208        }
209        return null;
210
211    }
212
213    private void loadUserDatabase(InputStream in) throws IOException {
214        BufferedReader reader =
215            new BufferedReader(new InputStreamReader(in, "UTF-8"));
216        Lock lock = userDbLock.writeLock();
217        lock.lock();
218        try {
219            userDb.clear();
220
221            String line;
222            while ((line = reader.readLine()) != null) {
223                line = line.trim();
224                if (line.isEmpty() || line.startsWith("#")) {
225                    continue;
226                }
227                int pos = line.indexOf(':');
228                if ((pos != -1) && (pos < (line.length() - 2))) {
229                    String username = line.substring(0, pos).trim();
230                    int pos2 = line.indexOf(':', pos + 1);
231                    String password = null;
232                    String attrs = null;
233                    if ((pos2 != -1) && (pos2 < (line.length() -2))) {
234                        password = line.substring(pos + 1, pos2).trim();
235                        attrs = line.substring(pos2 + 1).trim();
236                    } else {
237                        password = line.substring(pos + 1).trim();
238                    }
239
240                    if ((username != null) && (password != null)) {
241                        if (!username.isEmpty() && !password.isEmpty()) {
242                            HashMethod method = HashMethod.PLAIN;
243                            if (password.startsWith("{")) {
244                                pos = password.indexOf('}', 1);
245                                if ((pos != -1) &&
246                                        (pos < password.length() - 2)) {
247                                    method = HashMethod.fromString(password
248                                            .substring(1, pos));
249                                    password = password.substring(pos + 1)
250                                            .toLowerCase();
251                                }
252                            }
253                            if (method != null) {
254                                Map<String, String> attributes = null;
255                                if (attrs != null) {
256                                    attributes = parseAttributes(attrs);
257                                }
258                                userDb.put(username, new Entry(method,
259                                        username, password, attributes));
260                                continue;
261                            }
262                        }
263                    }
264                }
265                // FIXME: better logging?
266                System.err.println("MALFORMED LINE: " + line);
267            }
268        } finally {
269            lock.unlock();
270        }
271        reader.close();
272    }
273
274    private Map<String, String> parseAttributes(String s) {
275        Map<String, String> attributes = null;
276        String[] attrs = s.split("\\s*,\\s*");
277        if ((attrs != null) && (attrs.length > 0)) {
278            Pattern pattern = Pattern.compile("(\\S+)\\s*=\\s*\"([^\"]+)\"");
279            for (String attr : attrs) {
280                Matcher m = pattern.matcher(attr);
281                if (m.matches()) {
282                    if (attributes == null) {
283                        attributes = new HashMap<String,String>(attrs.length);
284                    }
285                    attributes.put(m.group(1).toLowerCase(), m.group(2));
286                } else {
287                    System.err.println("MALFORMED ATTRIBUTE: " + attr);
288                }
289            }
290        }
291        return attributes;
292    }
293
294} // class BasicAuthStrategy
Note: See TracBrowser for help on using the repository browser.