• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IQSS / dataverse / #21098

04 Jan 2024 08:52PM CUT coverage: 20.164% (-0.001%) from 20.165%
#21098

push

github

landreev
A quick-and-dumb experiment - how much is it going to speed up the versions tab, if
we look up the entire versions-filemetadatas tree with the new findDeep() method. #8328
spoiler: doesn't fix it really, it's still a hog.

16638 of 82513 relevant lines covered (20.16%)

0.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/edu/harvard/iq/dataverse/api/ApiBlockingFilter.java
1
package edu.harvard.iq.dataverse.api;
2

3
import edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip.IpAddress;
4
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
5
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
6
import java.io.IOException;
7
import java.util.Map;
8
import java.util.Set;
9
import java.util.TreeMap;
10
import java.util.TreeSet;
11
import java.util.logging.Level;
12
import java.util.logging.Logger;
13
import jakarta.ejb.EJB;
14
import jakarta.servlet.Filter;
15
import jakarta.servlet.FilterChain;
16
import jakarta.servlet.FilterConfig;
17
import jakarta.servlet.ServletException;
18
import jakarta.servlet.ServletRequest;
19
import jakarta.servlet.ServletResponse;
20
import jakarta.servlet.http.HttpServletRequest;
21
import jakarta.servlet.http.HttpServletResponse;
22

23

24
/**
25
 * A web filter to block API administration calls.
26
 * @author michael
27
 */
28
public class ApiBlockingFilter implements Filter {
×
29
    public static final String UNBLOCK_KEY_QUERYPARAM = "unblock-key";
30
            
31
    interface BlockPolicy {
32
        public void doBlock(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException;
33
    }
34
    
35
    /**
36
     * A policy that allows all requests.
37
     */
38
    private static final BlockPolicy ALLOW = new BlockPolicy(){
×
39
        @Override
40
        public void doBlock(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
41
            fc.doFilter(sr, sr1);
×
42
        }
×
43
    };
44
    
45
    /**
46
     * A policy that drops blocked requests.
47
     */
48
    private static final BlockPolicy DROP = new BlockPolicy(){
×
49
        @Override
50
        public void doBlock(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
51
            HttpServletResponse httpResponse = (HttpServletResponse) sr1;
×
52
            httpResponse.getWriter().println("{ status:\"error\", message:\"Endpoint blocked. Please contact the dataverse administrator\"}" );
×
53
            httpResponse.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
×
54
            httpResponse.setContentType("application/json");
×
55
        }
×
56
    };
57
    
58
    /**
59
     * Allow only from localhost.
60
     */
61
    private static final BlockPolicy LOCAL_HOST_ONLY = new BlockPolicy() {
×
62

63
        @Override
64
        public void doBlock(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
65
            IpAddress origin = new DataverseRequest( null, (HttpServletRequest)sr ).getSourceAddress();
×
66
            if ( origin.isLocalhost() ) {
×
67
                fc.doFilter(sr, sr1);
×
68
            } else {
69
                HttpServletResponse httpResponse = (HttpServletResponse) sr1;
×
70
                httpResponse.getWriter().println("{ status:\"error\", message:\"Endpoint available from localhost only. Please contact the dataverse administrator\"}" );
×
71
                httpResponse.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
×
72
                httpResponse.setContentType("application/json");
×
73
            }
74
        }
×
75
    };
76
      
77
    /**
78
     * Allow only for requests that have the {@link #UNBLOCK_KEY_QUERYPARAM} param with
79
     * value from {@link SettingsServiceBean.Key.BlockedApiKey}
80
     */
81
    private final BlockPolicy unblockKey = new BlockPolicy() {
×
82

83
        @Override
84
        public void doBlock(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
85
            boolean block = true;
×
86
            
87
            String masterKey = settingsSvc.getValueForKey(SettingsServiceBean.Key.BlockedApiKey);
×
88
            if ( masterKey != null ) {
×
89
                String queryString = ((HttpServletRequest)sr).getQueryString();
×
90
                if ( queryString != null ) {
×
91
                    for ( String paramPair : queryString.split("&") ) {
×
92
                        String[] curPair = paramPair.split("=",-1);
×
93
                        if ( (curPair.length >= 2 )
×
94
                               && UNBLOCK_KEY_QUERYPARAM.equals(curPair[0])
×
95
                               && masterKey.equals(curPair[1]) ) {
×
96
                            block = false;
×
97
                            break;
×
98
                        }
99
                    }
100
                }
101
            }
102
            
103
            if ( block ) {
×
104
                HttpServletResponse httpResponse = (HttpServletResponse) sr1;
×
105
                httpResponse.getWriter().println("{ status:\"error\", message:\"Endpoint available using API key only. Please contact the dataverse administrator\"}" );
×
106
                httpResponse.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
×
107
                httpResponse.setContentType("application/json");
×
108
            } else {
×
109
                fc.doFilter(sr, sr1);
×
110
            }
111
        }
×
112
    };
113
    
114
    private static final Logger logger = Logger.getLogger(ApiBlockingFilter.class.getName());
×
115
    
116
    @EJB
117
    protected SettingsServiceBean settingsSvc;
118
    
119
    final Set<String> blockedApiEndpoints = new TreeSet<>();
×
120
    private String lastEndpointList;
121
    private final Map<String, BlockPolicy> policies = new TreeMap<>();
×
122
    
123
    @Override
124
    public void init(FilterConfig fc) throws ServletException {
125
        updateBlockedPoints();
×
126
        policies.put("allow", ALLOW);
×
127
        policies.put("drop", DROP);
×
128
        policies.put("localhost-only", LOCAL_HOST_ONLY);
×
129
        policies.put("unblock-key", unblockKey);
×
130
    }
×
131

132
    private void updateBlockedPoints() {
133
        blockedApiEndpoints.clear();
×
134
        String endpointList = settingsSvc.getValueForKey(SettingsServiceBean.Key.BlockedApiEndpoints, "");
×
135
        for ( String endpoint : endpointList.split(",") ) {
×
136
            String endpointPrefix = canonize(endpoint);
×
137
            if ( ! endpointPrefix.isEmpty() ) {
×
138
                endpointPrefix = endpointPrefix + "/"; 
×
139
                logger.log(Level.INFO, "Blocking API endpoint: {0}", endpointPrefix);
×
140
                blockedApiEndpoints.add(endpointPrefix);
×
141
            }
142
        }
143
        lastEndpointList = endpointList;
×
144
    }
×
145

146
    @Override
147
    public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
148
        
149
        String endpointList = settingsSvc.getValueForKey(SettingsServiceBean.Key.BlockedApiEndpoints, "");
×
150
        if ( ! endpointList.equals(lastEndpointList) ) {
×
151
            updateBlockedPoints();
×
152
        }
153
        
154
        HttpServletRequest hsr = (HttpServletRequest) sr;
×
155
        String requestURI = hsr.getRequestURI();
×
156
        String apiEndpoint = canonize(requestURI.substring(hsr.getServletPath().length()));
×
157
        for ( String prefix : blockedApiEndpoints ) {
×
158
            if ( apiEndpoint.startsWith(prefix) ) {
×
159
                getBlockPolicy().doBlock(sr, sr1, fc);
×
160
                return;
×
161
            }
162
        }
×
163
        try {
164
            if (settingsSvc.isTrueForKey(SettingsServiceBean.Key.AllowCors, true )) {
×
165
                ((HttpServletResponse) sr1).addHeader("Access-Control-Allow-Origin", "*");
×
166
                ((HttpServletResponse) sr1).addHeader("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS");
×
167
                ((HttpServletResponse) sr1).addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, X-Dataverse-Key, Range");
×
168
                ((HttpServletResponse) sr1).addHeader("Access-Control-Expose-Headers", "Accept-Ranges, Content-Range, Content-Encoding");
×
169
            }
170
            fc.doFilter(sr, sr1);
×
171
        } catch ( ServletException se ) {
×
172
            logger.log(Level.WARNING, "Error processing " + requestURI +": " + se.getMessage(), se);
×
173
            HttpServletResponse resp = (HttpServletResponse) sr1;
×
174
            resp.setStatus(500);
×
175
            resp.setHeader("PROCUDER", "ApiBlockingFilter");
×
176
            resp.getWriter().append("Error: " + se.getMessage());
×
177
        }
×
178
    }
×
179
    
180
    @Override
181
    public void destroy() {}
×
182
    
183
    private BlockPolicy getBlockPolicy() {
184
        String blockPolicyName = settingsSvc.getValueForKey(SettingsServiceBean.Key.BlockedApiPolicy, "");
×
185
        BlockPolicy p = policies.get(blockPolicyName.trim());
×
186
        if ( p != null ) {
×
187
            return p;
×
188
        } else {
189
            logger.log(Level.WARNING, "Undefined block policy {0}. Available policies are {1}",
×
190
                    new Object[]{blockPolicyName, policies.keySet()});
×
191
            return ALLOW;
×
192
        }
193
    }
194
    
195
    /**
196
     * Creates a canonical representation of {@code in}: trimmed spaces and slashes
197
     * @param in the raw string
198
     * @return {@code in} with no trailing and leading spaces and slashes.
199
     */
200
    private String canonize( String in ) {
201
        in = in.trim();
×
202
        if ( in.startsWith("/") ) {
×
203
            in = in.substring(1);
×
204
        }
205
        if ( in.endsWith("/") ) {
×
206
            in = in.substring(0, in.length()-1);
×
207
        }
208
        return in;
×
209
    } 
210
    
211
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc