mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
@@ -5,6 +5,7 @@ import io.dropwizard.assets.AssetsBundle;
|
||||
import io.dropwizard.db.DataSourceFactory;
|
||||
import io.dropwizard.hibernate.HibernateBundle;
|
||||
import io.dropwizard.migrations.MigrationsBundle;
|
||||
import io.dropwizard.server.DefaultServerFactory;
|
||||
import io.dropwizard.servlets.CacheBustingFilter;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import io.dropwizard.setup.Environment;
|
||||
@@ -22,6 +23,7 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
|
||||
import com.commafeed.backend.feed.FeedRefreshTaskGiver;
|
||||
import com.commafeed.backend.feed.FeedRefreshUpdater;
|
||||
@@ -41,8 +43,7 @@ import com.commafeed.backend.service.StartupService;
|
||||
import com.commafeed.backend.service.UserService;
|
||||
import com.commafeed.backend.task.OldStatusesCleanupTask;
|
||||
import com.commafeed.backend.task.OrphansCleanupTask;
|
||||
import com.commafeed.frontend.auth.SecurityCheckProvider;
|
||||
import com.commafeed.frontend.auth.SecurityCheckProvider.SecurityCheckUserServiceProvider;
|
||||
import com.commafeed.frontend.auth.SecurityCheckFactoryProvider;
|
||||
import com.commafeed.frontend.resource.AdminREST;
|
||||
import com.commafeed.frontend.resource.CategoryREST;
|
||||
import com.commafeed.frontend.resource.EntryREST;
|
||||
@@ -54,10 +55,9 @@ import com.commafeed.frontend.servlet.AnalyticsServlet;
|
||||
import com.commafeed.frontend.servlet.CustomCssServlet;
|
||||
import com.commafeed.frontend.servlet.LogoutServlet;
|
||||
import com.commafeed.frontend.servlet.NextUnreadServlet;
|
||||
import com.commafeed.frontend.session.SessionHelperProvider;
|
||||
import com.commafeed.frontend.session.SessionHelperFactoryProvider;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.sun.jersey.api.core.ResourceConfig;
|
||||
import com.wordnik.swagger.config.ConfigFactory;
|
||||
import com.wordnik.swagger.config.ScannerFactory;
|
||||
import com.wordnik.swagger.config.SwaggerConfig;
|
||||
@@ -105,20 +105,20 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
|
||||
@Override
|
||||
public void run(CommaFeedConfiguration config, Environment environment) throws Exception {
|
||||
// configure context path
|
||||
environment.getApplicationContext().setContextPath(config.getApplicationSettings().getContextPath());
|
||||
|
||||
// guice init
|
||||
Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics()));
|
||||
|
||||
// Auth/session management
|
||||
// session management
|
||||
environment.servlets().setSessionHandler(new SessionHandler(config.getSessionManagerFactory().build()));
|
||||
environment.jersey().register(new SecurityCheckUserServiceProvider(injector.getInstance(UserService.class)));
|
||||
environment.jersey().register(SecurityCheckProvider.class);
|
||||
environment.jersey().register(SessionHelperProvider.class);
|
||||
|
||||
// support for "@SecurityCheck User user" intection
|
||||
environment.jersey().register(new SecurityCheckFactoryProvider.Binder(injector.getInstance(UserService.class)));
|
||||
// support for "@COntext SessionHelper sessionHelper" intection
|
||||
environment.jersey().register(new SessionHelperFactoryProvider.Binder());
|
||||
|
||||
// REST resources
|
||||
environment.jersey().setUrlPattern("/rest/*");
|
||||
((DefaultServerFactory) config.getServerFactory()).setJerseyRootPath("/rest/*");
|
||||
environment.jersey().register(injector.getInstance(AdminREST.class));
|
||||
environment.jersey().register(injector.getInstance(CategoryREST.class));
|
||||
environment.jersey().register(injector.getInstance(EntryREST.class));
|
||||
@@ -127,6 +127,9 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
environment.jersey().register(injector.getInstance(ServerREST.class));
|
||||
environment.jersey().register(injector.getInstance(UserREST.class));
|
||||
|
||||
// @FormDataParam support
|
||||
environment.jersey().register(MultiPartFeature.class);
|
||||
|
||||
// Servlets
|
||||
environment.servlets().addServlet("next", injector.getInstance(NextUnreadServlet.class)).addMapping("/next");
|
||||
environment.servlets().addServlet("logout", injector.getInstance(LogoutServlet.class)).addMapping("/logout");
|
||||
@@ -170,8 +173,6 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||
}
|
||||
}).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/rest/*");
|
||||
|
||||
// enable wadl
|
||||
environment.jersey().disable(ResourceConfig.FEATURE_DISABLE_WADL);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
@@ -12,7 +12,7 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
import com.commafeed.backend.cache.RedisPoolFactory;
|
||||
@@ -62,10 +62,6 @@ public class CommaFeedConfiguration extends Configuration {
|
||||
|
||||
@Getter
|
||||
public static class ApplicationSettings {
|
||||
@NotNull
|
||||
@NotBlank
|
||||
private String contextPath;
|
||||
|
||||
@NotNull
|
||||
@NotBlank
|
||||
private String publicUrl;
|
||||
|
||||
@@ -11,8 +11,8 @@ import com.commafeed.CommaFeedConfiguration.CacheType;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.cache.NoopCacheService;
|
||||
import com.commafeed.backend.cache.RedisCacheService;
|
||||
import com.commafeed.backend.favicon.DefaultFaviconFetcher;
|
||||
import com.commafeed.backend.favicon.AbstractFaviconFetcher;
|
||||
import com.commafeed.backend.favicon.DefaultFaviconFetcher;
|
||||
import com.commafeed.backend.favicon.YoutubeFaviconFetcher;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
@@ -17,7 +17,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
@@ -53,7 +53,7 @@ public class HttpGetter {
|
||||
private static final String ACCEPT_LANGUAGE = "en";
|
||||
private static final String PRAGMA_NO_CACHE = "No-cache";
|
||||
private static final String CACHE_CONTROL_NO_CACHE = "no-cache";
|
||||
|
||||
|
||||
private static final HttpResponseInterceptor REMOVE_INCORRECT_CONTENT_ENCODING = new ContentEncodingInterceptor();
|
||||
|
||||
private static SSLContext SSL_CONTEXT = null;
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.commafeed.backend.cache;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.commafeed.backend.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.FeedCategory;
|
||||
@@ -70,7 +70,7 @@ public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||
}
|
||||
boolean isChild = false;
|
||||
while (child != null) {
|
||||
if (ObjectUtils.equals(child.getId(), parent.getId())) {
|
||||
if (Objects.equals(child.getId(), parent.getId())) {
|
||||
isChild = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@@ -7,8 +7,8 @@ import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import javax.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
@@ -45,7 +45,7 @@ public class FeedFetcher {
|
||||
} catch (FeedException e) {
|
||||
if (extractFeedUrlFromHtml) {
|
||||
String extractedUrl = extractFeedUrl(StringUtils.newStringUtf8(result.getContent()), feedUrl);
|
||||
if (org.apache.commons.lang.StringUtils.isNotBlank(extractedUrl)) {
|
||||
if (org.apache.commons.lang3.StringUtils.isNotBlank(extractedUrl)) {
|
||||
feedUrl = extractedUrl;
|
||||
|
||||
result = getter.getBinary(extractedUrl, lastModified, eTag, timeout);
|
||||
|
||||
@@ -11,8 +11,7 @@ import javax.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jdom2.Element;
|
||||
import org.jdom2.Namespace;
|
||||
import org.xml.sax.InputSource;
|
||||
@@ -173,7 +172,7 @@ public class FeedParser {
|
||||
if (item.getContents().isEmpty()) {
|
||||
content = item.getDescription() == null ? null : item.getDescription().getValue();
|
||||
} else {
|
||||
content = StringUtils.join(Collections2.transform(item.getContents(), CONTENT_TO_STRING), SystemUtils.LINE_SEPARATOR);
|
||||
content = StringUtils.join(Collections2.transform(item.getContents(), CONTENT_TO_STRING), System.lineSeparator());
|
||||
}
|
||||
return StringUtils.trimToNull(content);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import com.codahale.metrics.Gauge;
|
||||
import com.codahale.metrics.Meter;
|
||||
|
||||
@@ -16,8 +16,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
|
||||
@@ -11,8 +11,8 @@ import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
|
||||
@@ -13,9 +13,9 @@ import java.util.regex.Pattern;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
@@ -517,4 +517,5 @@ public class FeedUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import javax.persistence.TemporalType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.annotations.Cascade;
|
||||
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
@@ -78,7 +78,7 @@ public class User extends AbstractModel {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean shouldRefreshFeedsAt(Date when) {
|
||||
return (lastFullRefresh == null || lastFullRefreshMoreThan30MinutesBefore(when));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
|
||||
@@ -6,7 +6,7 @@ import javax.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
||||
import com.commafeed.backend.feed.FeedUtils;
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
|
||||
@@ -15,7 +15,7 @@ import javax.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
// taken from http://www.javacodegeeks.com/2012/05/secure-password-storage-donts-dos-and.html
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
@@ -10,7 +10,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
|
||||
@@ -10,7 +10,7 @@ import javax.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
@@ -33,7 +33,7 @@ public class UserService {
|
||||
|
||||
private final PasswordEncryptionService encryptionService;
|
||||
private final CommaFeedConfiguration config;
|
||||
|
||||
|
||||
private final PostLoginActivities postLoginActivities;
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ public class UserService {
|
||||
}
|
||||
}
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* try to log in with given api key
|
||||
|
||||
@@ -7,7 +7,7 @@ import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.dao.UserDAO;
|
||||
@@ -17,11 +17,11 @@ import com.commafeed.backend.service.FeedSubscriptionService;
|
||||
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||
@Singleton
|
||||
public class PostLoginActivities {
|
||||
|
||||
|
||||
private final UserDAO userDAO;
|
||||
private final FeedSubscriptionService feedSubscriptionService;
|
||||
private final CommaFeedConfiguration config;
|
||||
|
||||
|
||||
public void executeFor(User user) {
|
||||
Date lastLogin = user.getLastLogin();
|
||||
Date now = new Date();
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.commafeed.frontend.auth;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
|
||||
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
import com.commafeed.backend.service.UserService;
|
||||
import com.commafeed.frontend.session.SessionHelper;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityCheckFactory extends AbstractContainerRequestValueFactory<User> {
|
||||
|
||||
private static final String PREFIX = "Basic";
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@Inject
|
||||
UserService userService;
|
||||
|
||||
private final Role role;
|
||||
private final boolean apiKeyAllowed;
|
||||
|
||||
@Override
|
||||
public User provide() {
|
||||
Optional<User> user = apiKeyLogin();
|
||||
if (!user.isPresent()) {
|
||||
user = basicAuthenticationLogin();
|
||||
}
|
||||
if (!user.isPresent()) {
|
||||
user = cookieSessionLogin(new SessionHelper(request));
|
||||
}
|
||||
|
||||
if (user.isPresent()) {
|
||||
if (user.get().hasRole(role)) {
|
||||
return user.get();
|
||||
} else {
|
||||
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN)
|
||||
.entity("You don't have the required role to access this resource.").type(MediaType.TEXT_PLAIN_TYPE).build());
|
||||
}
|
||||
} else {
|
||||
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
|
||||
.entity("Credentials are required to access this resource.").type(MediaType.TEXT_PLAIN_TYPE).build());
|
||||
}
|
||||
}
|
||||
|
||||
Optional<User> cookieSessionLogin(SessionHelper sessionHelper) {
|
||||
Optional<User> loggedInUser = sessionHelper.getLoggedInUser();
|
||||
if (loggedInUser.isPresent()) {
|
||||
userService.performPostLoginActivities(loggedInUser.get());
|
||||
}
|
||||
return loggedInUser;
|
||||
}
|
||||
|
||||
private Optional<User> basicAuthenticationLogin() {
|
||||
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if (header != null) {
|
||||
int space = header.indexOf(' ');
|
||||
if (space > 0) {
|
||||
String method = header.substring(0, space);
|
||||
if (PREFIX.equalsIgnoreCase(method)) {
|
||||
String decoded = B64Code.decode(header.substring(space + 1), StringUtil.__ISO_8859_1);
|
||||
int i = decoded.indexOf(':');
|
||||
if (i > 0) {
|
||||
String username = decoded.substring(0, i);
|
||||
String password = decoded.substring(i + 1);
|
||||
return userService.login(username, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
private Optional<User> apiKeyLogin() {
|
||||
String apiKey = request.getParameter("apiKey");
|
||||
if (apiKey != null && apiKeyAllowed) {
|
||||
return userService.login(apiKey);
|
||||
}
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.commafeed.frontend.auth;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.glassfish.hk2.api.Factory;
|
||||
import org.glassfish.hk2.api.InjectionResolver;
|
||||
import org.glassfish.hk2.api.ServiceLocator;
|
||||
import org.glassfish.hk2.api.TypeLiteral;
|
||||
import org.glassfish.hk2.utilities.binding.AbstractBinder;
|
||||
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
|
||||
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
|
||||
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
|
||||
import org.glassfish.jersey.server.model.Parameter;
|
||||
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
|
||||
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.service.UserService;
|
||||
|
||||
@Singleton
|
||||
public class SecurityCheckFactoryProvider extends AbstractValueFactoryProvider {
|
||||
|
||||
@Inject
|
||||
public SecurityCheckFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, final ServiceLocator injector) {
|
||||
super(extractorProvider, injector, Parameter.Source.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory<?> createValueFactory(final Parameter parameter) {
|
||||
final Class<?> classType = parameter.getRawType();
|
||||
|
||||
SecurityCheck securityCheck = parameter.getAnnotation(SecurityCheck.class);
|
||||
if (securityCheck == null)
|
||||
return null;
|
||||
|
||||
if (classType.isAssignableFrom(User.class)) {
|
||||
return new SecurityCheckFactory(securityCheck.value(), securityCheck.apiKeyAllowed());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SecurityCheckInjectionResolver extends ParamInjectionResolver<SecurityCheck> {
|
||||
public SecurityCheckInjectionResolver() {
|
||||
super(SecurityCheckFactoryProvider.class);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class Binder extends AbstractBinder {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SecurityCheckFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
|
||||
bind(SecurityCheckInjectionResolver.class).to(new TypeLiteral<InjectionResolver<SecurityCheck>>() {
|
||||
}).in(Singleton.class);
|
||||
bind(userService).to(UserService.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package com.commafeed.frontend.auth;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserRole.Role;
|
||||
import com.commafeed.backend.service.UserService;
|
||||
import com.commafeed.frontend.session.SessionHelper;
|
||||
import com.google.common.base.Optional;
|
||||
import com.sun.jersey.api.core.HttpContext;
|
||||
import com.sun.jersey.api.model.Parameter;
|
||||
import com.sun.jersey.core.spi.component.ComponentContext;
|
||||
import com.sun.jersey.core.spi.component.ComponentScope;
|
||||
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
|
||||
import com.sun.jersey.spi.inject.Injectable;
|
||||
import com.sun.jersey.spi.inject.InjectableProvider;
|
||||
import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
|
||||
|
||||
public class SecurityCheckProvider implements InjectableProvider<SecurityCheck, Parameter> {
|
||||
|
||||
public static class SecurityCheckUserServiceProvider extends SingletonTypeInjectableProvider<Context, UserService> {
|
||||
|
||||
public SecurityCheckUserServiceProvider(UserService userService) {
|
||||
super(UserService.class, userService);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class SecurityCheckInjectable extends AbstractHttpContextInjectable<User> {
|
||||
private static final String PREFIX = "Basic";
|
||||
|
||||
private final SessionHelper sessionHelper;
|
||||
private final UserService userService;
|
||||
private final Role role;
|
||||
private final boolean apiKeyAllowed;
|
||||
|
||||
@Override
|
||||
public User getValue(HttpContext c) {
|
||||
Optional<User> user = apiKeyLogin(c);
|
||||
if (!user.isPresent()) {
|
||||
user = basicAuthenticationLogin(c);
|
||||
}
|
||||
if (!user.isPresent()) {
|
||||
user = cookieSessionLogin();
|
||||
}
|
||||
|
||||
if (user.isPresent()) {
|
||||
if (user.get().hasRole(role)) {
|
||||
return user.get();
|
||||
} else {
|
||||
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN)
|
||||
.entity("You don't have the required role to access this resource.").type(MediaType.TEXT_PLAIN_TYPE).build());
|
||||
}
|
||||
} else {
|
||||
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
|
||||
.entity("Credentials are required to access this resource.").type(MediaType.TEXT_PLAIN_TYPE).build());
|
||||
}
|
||||
}
|
||||
|
||||
Optional<User> cookieSessionLogin() {
|
||||
Optional<User> loggedInUser = sessionHelper.getLoggedInUser();
|
||||
if (loggedInUser.isPresent()) {
|
||||
userService.performPostLoginActivities(loggedInUser.get());
|
||||
}
|
||||
return loggedInUser;
|
||||
}
|
||||
|
||||
private Optional<User> basicAuthenticationLogin(HttpContext c) {
|
||||
String header = c.getRequest().getHeaderValue(HttpHeaders.AUTHORIZATION);
|
||||
if (header != null) {
|
||||
int space = header.indexOf(' ');
|
||||
if (space > 0) {
|
||||
String method = header.substring(0, space);
|
||||
if (PREFIX.equalsIgnoreCase(method)) {
|
||||
String decoded = B64Code.decode(header.substring(space + 1), StringUtil.__ISO_8859_1);
|
||||
int i = decoded.indexOf(':');
|
||||
if (i > 0) {
|
||||
String username = decoded.substring(0, i);
|
||||
String password = decoded.substring(i + 1);
|
||||
return userService.login(username, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
private Optional<User> apiKeyLogin(HttpContext c) {
|
||||
String apiKey = c.getUriInfo().getQueryParameters().getFirst("apiKey");
|
||||
if (apiKey != null && apiKeyAllowed) {
|
||||
return userService.login(apiKey);
|
||||
}
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
private SessionHelper sessionHelper;
|
||||
private UserService userService;
|
||||
|
||||
public SecurityCheckProvider(@Context HttpServletRequest req, @Context UserService userService) {
|
||||
this.sessionHelper = new SessionHelper(req);
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentScope getScope() {
|
||||
return ComponentScope.PerRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injectable<?> getInjectable(ComponentContext ic, SecurityCheck sc, Parameter c) {
|
||||
return new SecurityCheckInjectable(sessionHelper, userService, sc.value(), sc.apiKeyAllowed());
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedApplication;
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -27,8 +28,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.cache.CacheService;
|
||||
@@ -360,7 +361,7 @@ public class CategoryREST {
|
||||
|
||||
int existingIndex = -1;
|
||||
for (int i = 0; i < categories.size(); i++) {
|
||||
if (ObjectUtils.equals(categories.get(i).getId(), category.getId())) {
|
||||
if (Objects.equals(categories.get(i).getId(), category.getId())) {
|
||||
existingIndex = i;
|
||||
}
|
||||
}
|
||||
@@ -437,7 +438,7 @@ public class CategoryREST {
|
||||
category.setExpanded(true);
|
||||
|
||||
for (FeedCategory c : categories) {
|
||||
if ((id == null && c.getParent() == null) || (c.getParent() != null && ObjectUtils.equals(c.getParent().getId(), id))) {
|
||||
if ((id == null && c.getParent() == null) || (c.getParent() != null && Objects.equals(c.getParent().getId(), id))) {
|
||||
Category child = buildCategory(c.getId(), categories, subscriptions, unreadCount);
|
||||
child.setId(String.valueOf(c.getId()));
|
||||
child.setName(c.getName());
|
||||
@@ -458,7 +459,7 @@ public class CategoryREST {
|
||||
|
||||
for (FeedSubscription subscription : subscriptions) {
|
||||
if ((id == null && subscription.getCategory() == null)
|
||||
|| (subscription.getCategory() != null && ObjectUtils.equals(subscription.getCategory().getId(), id))) {
|
||||
|| (subscription.getCategory() != null && Objects.equals(subscription.getCategory().getId(), id))) {
|
||||
UnreadCount uc = unreadCount.get(subscription.getId());
|
||||
Subscription sub = Subscription.build(subscription, config.getApplicationSettings().getPublicUrl(), uc);
|
||||
category.getFeeds().add(sub);
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -33,8 +34,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
|
||||
import com.commafeed.CommaFeedApplication;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
@@ -78,7 +80,6 @@ import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.feed.synd.SyndFeedImpl;
|
||||
import com.rometools.rome.io.SyndFeedOutput;
|
||||
import com.rometools.rome.io.WireFeedOutput;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
import com.wordnik.swagger.annotations.Api;
|
||||
import com.wordnik.swagger.annotations.ApiOperation;
|
||||
import com.wordnik.swagger.annotations.ApiParam;
|
||||
@@ -440,7 +441,7 @@ public class FeedREST {
|
||||
|
||||
int existingIndex = -1;
|
||||
for (int i = 0; i < subs.size(); i++) {
|
||||
if (ObjectUtils.equals(subs.get(i).getId(), subscription.getId())) {
|
||||
if (Objects.equals(subs.get(i).getId(), subscription.getId())) {
|
||||
existingIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
|
||||
@@ -15,7 +15,7 @@ import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
|
||||
@@ -27,9 +27,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
||||
import com.commafeed.CommaFeedApplication;
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.commafeed.frontend.session;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
|
||||
|
||||
public class SessionHelperFactory extends AbstractContainerRequestValueFactory<SessionHelper> {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@Override
|
||||
public SessionHelper provide() {
|
||||
return new SessionHelper(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.commafeed.frontend.session;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import org.glassfish.hk2.api.Factory;
|
||||
import org.glassfish.hk2.api.InjectionResolver;
|
||||
import org.glassfish.hk2.api.ServiceLocator;
|
||||
import org.glassfish.hk2.api.TypeLiteral;
|
||||
import org.glassfish.hk2.utilities.binding.AbstractBinder;
|
||||
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
|
||||
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
|
||||
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
|
||||
import org.glassfish.jersey.server.model.Parameter;
|
||||
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
|
||||
|
||||
@Singleton
|
||||
public class SessionHelperFactoryProvider extends AbstractValueFactoryProvider {
|
||||
|
||||
@Inject
|
||||
public SessionHelperFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, final ServiceLocator injector) {
|
||||
super(extractorProvider, injector, Parameter.Source.CONTEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory<?> createValueFactory(final Parameter parameter) {
|
||||
final Class<?> classType = parameter.getRawType();
|
||||
|
||||
Context context = parameter.getAnnotation(Context.class);
|
||||
if (context == null)
|
||||
return null;
|
||||
|
||||
if (classType.isAssignableFrom(SessionHelper.class)) {
|
||||
return new SessionHelperFactory();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SessionHelperInjectionResolver extends ParamInjectionResolver<Context> {
|
||||
public SessionHelperInjectionResolver() {
|
||||
super(SessionHelperFactoryProvider.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Binder extends AbstractBinder {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SessionHelperFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
|
||||
bind(SessionHelperInjectionResolver.class).to(new TypeLiteral<InjectionResolver<Context>>() {
|
||||
}).in(Singleton.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.commafeed.frontend.session;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import com.sun.jersey.core.spi.component.ComponentContext;
|
||||
import com.sun.jersey.core.spi.component.ComponentScope;
|
||||
import com.sun.jersey.spi.inject.Injectable;
|
||||
import com.sun.jersey.spi.inject.InjectableProvider;
|
||||
|
||||
@Provider
|
||||
public class SessionHelperProvider implements InjectableProvider<Context, Type> {
|
||||
|
||||
private final ThreadLocal<HttpServletRequest> request;
|
||||
|
||||
public SessionHelperProvider(@Context ThreadLocal<HttpServletRequest> request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentScope getScope() {
|
||||
return ComponentScope.PerRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injectable<?> getInjectable(ComponentContext ic, final Context session, Type type) {
|
||||
if (type.equals(SessionHelper.class)) {
|
||||
return new Injectable<SessionHelper>() {
|
||||
@Override
|
||||
public SessionHelper getValue() {
|
||||
final HttpServletRequest req = request.get();
|
||||
if (req != null) {
|
||||
return new SessionHelper(req);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,11 @@ import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* See http://en.wikipedia.org/wiki/URL_normalization for a reference Note: some
|
||||
* parts of the code are adapted from: http://stackoverflow.com/a/4057470/405418
|
||||
* See http://en.wikipedia.org/wiki/URL_normalization for a reference Note: some parts of the code are adapted from:
|
||||
* http://stackoverflow.com/a/4057470/405418
|
||||
*
|
||||
* @author Yasser Ganjisaffar <lastname at gmail dot com>
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ public class URLCanonicalizer {
|
||||
|
||||
try {
|
||||
URL canonicalURL = new URL(UrlResolver.resolveUrl(context == null ? "" : context, href));
|
||||
|
||||
|
||||
String host = canonicalURL.getHost().toLowerCase();
|
||||
if (StringUtils.isBlank(host)) {
|
||||
// This is an invalid Url.
|
||||
@@ -113,7 +113,7 @@ public class URLCanonicalizer {
|
||||
|
||||
URL result = new URL(protocol, host, port, pathAndQueryString);
|
||||
return result.toExternalForm();
|
||||
|
||||
|
||||
} catch (MalformedURLException ex) {
|
||||
return null;
|
||||
} catch (URISyntaxException ex) {
|
||||
@@ -122,8 +122,7 @@ public class URLCanonicalizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a query string, separates the constituent name-value pairs, and
|
||||
* stores them in a SortedMap ordered by lexicographical order.
|
||||
* Takes a query string, separates the constituent name-value pairs, and stores them in a SortedMap ordered by lexicographical order.
|
||||
*
|
||||
* @return Null if there is no query string.
|
||||
*/
|
||||
@@ -149,7 +148,7 @@ public class URLCanonicalizer {
|
||||
params.put(tokens[0], "");
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 2:
|
||||
params.put(tokens[0], tokens[1]);
|
||||
break;
|
||||
}
|
||||
@@ -188,8 +187,7 @@ public class URLCanonicalizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Percent-encode values according the RFC 3986. The built-in Java
|
||||
* URLEncoder does not encode according to the RFC, so we make the extra
|
||||
* Percent-encode values according the RFC 3986. The built-in Java URLEncoder does not encode according to the RFC, so we make the extra
|
||||
* replacements.
|
||||
*
|
||||
* @param string
|
||||
|
||||
Reference in New Issue
Block a user