1 package com.atlassian.xwork.interceptors;
2
3 import com.opensymphony.xwork.interceptor.Interceptor;
4 import com.opensymphony.xwork.ActionInvocation;
5 import com.opensymphony.xwork.ActionSupport;
6 import com.opensymphony.xwork.Action;
7 import com.opensymphony.xwork.ValidationAware;
8 import com.opensymphony.webwork.ServletActionContext;
9 import com.atlassian.xwork.RequireSecurityToken;
10 import com.atlassian.xwork.SimpleXsrfTokenGenerator;
11 import com.atlassian.xwork.XsrfTokenGenerator;
12 import com.atlassian.xwork.XWorkVersionSupport;
13
14 import java.lang.reflect.Method;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 public class XsrfTokenInterceptor implements Interceptor
34 {
35 public static final String REQUEST_PARAM_NAME = "atl_token";
36 public static final String CONFIG_PARAM_NAME = "RequireSecurityToken";
37 public static final String VALIDATION_FAILED_ERROR_KEY = "atlassian.xwork.xsrf.badtoken";
38 public static final String SECURITY_TOKEN_REQUIRED_ERROR_KEY = "atlassian.xwork.xsrf.notoken";
39 public static final String OVERRIDE_HEADER_NAME = "X-Atlassian-Token";
40 public static final String OVERRIDE_HEADER_VALUE = "no-check";
41
42 public static enum SecurityLevel
43 {
44
45 OPT_IN(false),
46
47 OPT_OUT(true);
48
49 private final boolean defaultProtection;
50
51 SecurityLevel(boolean defaultProtection)
52 {
53 this.defaultProtection = defaultProtection;
54 }
55
56 public boolean getDefaultProtection()
57 {
58 return defaultProtection;
59 }
60 }
61
62 private final XsrfTokenGenerator tokenGenerator;
63 private final XWorkVersionSupport versionSupport;
64
65 public XsrfTokenInterceptor(XWorkVersionSupport versionSupport)
66 {
67 this(new SimpleXsrfTokenGenerator(), versionSupport);
68 }
69
70 public XsrfTokenInterceptor(XsrfTokenGenerator tokenGenerator, XWorkVersionSupport versionSupport)
71 {
72 this.tokenGenerator = tokenGenerator;
73 this.versionSupport = versionSupport;
74 }
75
76 public String intercept(ActionInvocation invocation) throws Exception
77 {
78 Method invocationMethod = invocation.getProxy().getConfig().getMethod();
79 String configParam = (String) invocation.getProxy().getConfig().getParams().get(CONFIG_PARAM_NAME);
80 RequireSecurityToken annotation = invocationMethod.getAnnotation(RequireSecurityToken.class);
81
82 boolean isProtected = methodRequiresProtection(configParam, annotation);
83 String token = ServletActionContext.getRequest().getParameter(REQUEST_PARAM_NAME);
84 boolean validToken = tokenGenerator.validateToken(ServletActionContext.getRequest(), token);
85
86 if (isProtected && !validToken)
87 {
88 if (token == null)
89 {
90 addInvalidTokenError(versionSupport.extractAction(invocation), SECURITY_TOKEN_REQUIRED_ERROR_KEY);
91 }
92 else
93 {
94 addInvalidTokenError(versionSupport.extractAction(invocation), VALIDATION_FAILED_ERROR_KEY);
95 }
96 ServletActionContext.getResponse().setStatus(403);
97 return ActionSupport.INPUT;
98 }
99
100 return invocation.invoke();
101 }
102
103 private boolean methodRequiresProtection(String configParam, RequireSecurityToken annotation)
104 {
105 if (isOverrideHeaderPresent())
106 return false;
107 if (configParam != null)
108 return Boolean.valueOf(configParam);
109 else if (annotation != null)
110 return annotation.value();
111 else
112 return getSecurityLevel().getDefaultProtection();
113 }
114
115
116
117
118
119
120
121
122
123 protected void addInvalidTokenError(Action action, String errorMessageKey)
124 {
125 if (action instanceof ValidationAware)
126 ((ValidationAware)action).addActionError(internationaliseErrorMessage(action, errorMessageKey));
127 }
128
129
130
131
132
133
134
135
136
137
138 protected String internationaliseErrorMessage(Action action, String messageKey)
139 {
140 return messageKey;
141 }
142
143 private boolean isOverrideHeaderPresent()
144 {
145 return OVERRIDE_HEADER_VALUE.equals(ServletActionContext.getRequest().getHeader(OVERRIDE_HEADER_NAME));
146 }
147
148
149
150 public void destroy()
151 {
152 }
153
154 public void init()
155 {
156 }
157
158
159
160
161
162
163
164
165 protected SecurityLevel getSecurityLevel()
166 {
167 return SecurityLevel.OPT_IN;
168 }
169 }