mirror of
https://github.com/NationalSecurityAgency/timely.git
synced 2026-11-09 17:36:01 -10:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a420d9f89 | ||
|
|
2fbd306a0f | ||
|
|
ac841d3f75 | ||
|
|
674e1b0ab5 | ||
|
|
4eedb84547 | ||
|
|
bef86eac37 | ||
|
|
27bb16e403 |
No files matched your search
+26
-14
@@ -11,21 +11,33 @@
|
||||
|
||||
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
|
||||
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
|
||||
</beans:bean>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:map key-type="java.lang.String">
|
||||
<beans:entry key="CN=example.com">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:entry>
|
||||
</util:map>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:list>
|
||||
<beans:bean class="timely.auth.TimelyUser">
|
||||
<beans:constructor-arg name="subjectDn">
|
||||
<beans:value>CN=example.com</beans:value>
|
||||
</beans:constructor-arg>
|
||||
<beans:constructor-arg name="auths">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:constructor-arg>
|
||||
<beans:constructor-arg name="roles">
|
||||
<util:list>
|
||||
<beans:value>G</beans:value>
|
||||
<beans:value>H</beans:value>
|
||||
<beans:value>I</beans:value>
|
||||
</util:list>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
</util:list>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
|
||||
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
|
||||
|
||||
@@ -8,7 +8,8 @@ public class ClientSsl {
|
||||
private String trustStoreFile;
|
||||
private String trustStoreType;
|
||||
private String trustStorePassword;
|
||||
private boolean twoWaySsl;
|
||||
private boolean useClientCert = true;
|
||||
private boolean hostVerificationEnabled = true;
|
||||
|
||||
public String getKeyFile() {
|
||||
return keyFile;
|
||||
@@ -58,7 +59,19 @@ public class ClientSsl {
|
||||
this.trustStorePassword = trustStorePassword;
|
||||
}
|
||||
|
||||
public boolean isTwoWaySsl() {
|
||||
return twoWaySsl;
|
||||
public void setUseClientCert(boolean useClientCert) {
|
||||
this.useClientCert = useClientCert;
|
||||
}
|
||||
|
||||
public boolean isUseClientCert() {
|
||||
return useClientCert;
|
||||
}
|
||||
|
||||
public void setHostVerificationEnabled(boolean hostVerificationEnabled) {
|
||||
this.hostVerificationEnabled = hostVerificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isHostVerificationEnabled() {
|
||||
return hostVerificationEnabled;
|
||||
}
|
||||
}
|
||||
@@ -13,18 +13,19 @@ import timely.balancer.connection.TimelyBalancedHost;
|
||||
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
|
||||
|
||||
private final SSLContext sslContext;
|
||||
private boolean twoWaySsl;
|
||||
private final BalancerConfiguration balancerConfig;
|
||||
|
||||
public HttpClientFactory(BalancerConfiguration balancerConfig, SSLContext sslContext) {
|
||||
this.balancerConfig = balancerConfig;
|
||||
this.sslContext = sslContext;
|
||||
this.twoWaySsl = balancerConfig.getSecurity().getClientSsl().isTwoWaySsl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<CloseableHttpClient> makeObject(TimelyBalancedHost k) throws Exception {
|
||||
BasicCookieStore cookieJar = new BasicCookieStore();
|
||||
return new DefaultPooledObject<>(
|
||||
timely.client.http.HttpClient.get(this.sslContext, cookieJar, false, twoWaySsl));
|
||||
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, cookieJar,
|
||||
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(),
|
||||
balancerConfig.getSecurity().getClientSsl().isUseClientCert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,15 +12,11 @@ import timely.client.websocket.subscription.WebSocketSubscriptionClient;
|
||||
public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, WebSocketSubscriptionClient> {
|
||||
|
||||
private final SSLContext sslContext;
|
||||
private final boolean doLogin;
|
||||
private final boolean twoWaySsl;
|
||||
private BalancerConfiguration balancerConfig;
|
||||
|
||||
public WsClientFactory(BalancerConfiguration balancerConfig, SSLContext sslContext) {
|
||||
this.sslContext = sslContext;
|
||||
this.doLogin = balancerConfig.isLoginRequired();
|
||||
this.balancerConfig = balancerConfig;
|
||||
this.twoWaySsl = balancerConfig.getSecurity().getClientSsl().isTwoWaySsl();
|
||||
this.sslContext = sslContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,7 +24,9 @@ public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedH
|
||||
|
||||
int bufferSize = balancerConfig.getWebsocket().getSubscriptionBatchSize() * 500;
|
||||
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
|
||||
k.getWsPort(), twoWaySsl, doLogin, "", "", false, bufferSize);
|
||||
k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(),
|
||||
balancerConfig.isLoginRequired(), "", "",
|
||||
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(), bufferSize);
|
||||
return new DefaultPooledObject<>(client);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
@@ -40,18 +41,20 @@ public class WsClientHandler extends ClientHandler {
|
||||
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> headers) {
|
||||
Multimap<String, String> requestHeaders = token.getHttpHeaders();
|
||||
if (token.getClientCert() != null) {
|
||||
ProxiedEntityUtils.addProxyHeaders(requestHeaders, token.getClientCert());
|
||||
Multimap<String, String> proxyRequestHeaders = HashMultimap.create();
|
||||
ProxiedEntityUtils.addProxyHeaders(proxyRequestHeaders, token.getClientCert());
|
||||
for (String s : proxyRequestHeaders.keySet()) {
|
||||
headers.put(s, new ArrayList(proxyRequestHeaders.get(s)));
|
||||
}
|
||||
}
|
||||
for (String s : requestHeaders.keySet()) {
|
||||
List<String> valueList = new ArrayList<>();
|
||||
valueList.addAll(requestHeaders.get(s));
|
||||
if (headers.containsKey(s)) {
|
||||
// add pre-existing values
|
||||
valueList.addAll(headers.get(s));
|
||||
|
||||
Multimap<String, String> originalRequestHeaders = token.getHttpHeaders();
|
||||
for (String s : originalRequestHeaders.keySet()) {
|
||||
if (!headers.containsKey(s)) {
|
||||
// add pre-existing values if key does not exist in proxyRequest
|
||||
headers.put(s, new ArrayList(originalRequestHeaders.get(s)));
|
||||
}
|
||||
headers.put(s, valueList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,9 +57,9 @@ public class HttpClient {
|
||||
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
|
||||
|
||||
if (clientAuth) {
|
||||
SSLConnectionSocketFactory sslCF = new SSLConnectionSocketFactory(ssl,
|
||||
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl,
|
||||
new String[] { "TLSv1.1", "TLSv1.2" }, null, hostnameVerifier);
|
||||
builder.setSSLSocketFactory(sslCF);
|
||||
builder.setSSLSocketFactory(sslCSF);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ public class WebSocketClient implements AutoCloseable {
|
||||
private final String timelyHostname;
|
||||
private final int timelyHttpsPort;
|
||||
private final int timelyWssPort;
|
||||
private final boolean clientAuth;
|
||||
private final boolean doLogin;
|
||||
private final boolean clientAuth;
|
||||
private final String timelyUsername;
|
||||
private final String timelyPassword;
|
||||
private final boolean hostVerificationEnabled;
|
||||
|
||||
@@ -23,7 +23,7 @@ def pivot(df, metric, groupByColumn=None):
|
||||
return dataFrame
|
||||
|
||||
|
||||
def resample(df, sample, how='mean'):
|
||||
def resample(df, sample, how='mean', interpolate=True, fill=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
|
||||
@@ -16,14 +16,15 @@ utc = UTC()
|
||||
class TimelyWebSocketClient(WebSocketClient):
|
||||
|
||||
def __init__(self, hostport, metric, tags, startTime, endTime, connect_timeout=WebSocketClient.DEFAULT_CONNECT_TIMEOUT,
|
||||
request_timeout=WebSocketClient.DEFAULT_REQUEST_TIMEOUT):
|
||||
request_timeout=WebSocketClient.DEFAULT_REQUEST_TIMEOUT, client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
|
||||
|
||||
self.metric = metric
|
||||
self.tags = tags
|
||||
self.startTime = startTime
|
||||
self.endTime = endTime
|
||||
self.subscriptionId = random.randint(1000000000, 9999999999)
|
||||
WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout)
|
||||
WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout,
|
||||
client_cert=client_cert, client_key=client_key, ca_certs=ca_certs, validate_cert=validate_cert)
|
||||
|
||||
def _on_message(self, msg):
|
||||
# implement in subclass
|
||||
@@ -72,7 +73,8 @@ class TimelyMetric(TimelyWebSocketClient):
|
||||
client = None
|
||||
endtime = None
|
||||
|
||||
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample):
|
||||
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample,
|
||||
client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
|
||||
self.series = pandas.Series()
|
||||
self.metric = metric
|
||||
self.tags = tags
|
||||
@@ -82,7 +84,8 @@ class TimelyMetric(TimelyWebSocketClient):
|
||||
self.data = []
|
||||
self.timeDateRange = TimeDateRange(beginStr, endStr, period)
|
||||
|
||||
TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs())
|
||||
TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs(),
|
||||
client_cert=client_cert, client_key=client_key, ca_certs=ca_certs, validate_cert=validate_cert)
|
||||
|
||||
def debugOn(self):
|
||||
self.debug = True
|
||||
|
||||
@@ -15,10 +15,14 @@ class WebSocketClient():
|
||||
APPLICATION_JSON = 'application/json'
|
||||
|
||||
def __init__(self, hostport, path, params, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
|
||||
request_timeout=DEFAULT_REQUEST_TIMEOUT):
|
||||
request_timeout=DEFAULT_REQUEST_TIMEOUT, client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
|
||||
|
||||
self.connect_timeout = connect_timeout
|
||||
self.request_timeout = request_timeout
|
||||
self.client_cert=client_cert
|
||||
self.client_key=client_key
|
||||
self.ca_certs=ca_certs
|
||||
self.validate_cert=validate_cert
|
||||
self.url = 'wss://' + hostport
|
||||
if (path is not None and len(path) > 0):
|
||||
if (path.startswith('/')):
|
||||
@@ -39,7 +43,8 @@ class WebSocketClient():
|
||||
"""Connect to the server.
|
||||
:param str url: server URL.
|
||||
"""
|
||||
request = websocket.httpclient.HTTPRequest(self.url, connect_timeout=self.connect_timeout, request_timeout=self.request_timeout, validate_cert=False)
|
||||
request = websocket.httpclient.HTTPRequest(self.url, connect_timeout=self.connect_timeout, request_timeout=self.request_timeout, validate_cert=self.validate_cert,
|
||||
client_cert=self.client_cert, client_key=self.client_key, ca_certs=self.ca_certs)
|
||||
websocket.websocket_connect(request, io_loop=ioloop, callback=self._connect_callback)
|
||||
|
||||
def send(self, data):
|
||||
|
||||
@@ -5,7 +5,9 @@ import os
|
||||
from timely.TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
|
||||
|
||||
hostport = "127.0.0.1:5244"
|
||||
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None).fetch()
|
||||
client_cert = None
|
||||
client_key = None
|
||||
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None, client_cert=client_cert, client_key=client_key).fetch()
|
||||
|
||||
print(timelyMetric.getDataFrame())
|
||||
exit()
|
||||
|
||||
+26
-10
@@ -15,15 +15,27 @@
|
||||
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:map key-type="java.lang.String">
|
||||
<beans:entry key="CN=example.com">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:entry>
|
||||
</util:map>
|
||||
<util:list>
|
||||
<beans:bean class="timely.auth.TimelyUser">
|
||||
<beans:constructor-arg name="subjectDn">
|
||||
<beans:value>CN=example.com</beans:value>
|
||||
</beans:constructor-arg>
|
||||
<beans:constructor-arg name="auths">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:constructor-arg>
|
||||
<beans:constructor-arg name="roles">
|
||||
<util:list>
|
||||
<beans:value>G</beans:value>
|
||||
<beans:value>H</beans:value>
|
||||
<beans:value>I</beans:value>
|
||||
</util:list>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
</util:list>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
@@ -45,5 +57,9 @@
|
||||
<!-- Basic authorization provider, validates provided username and password -->
|
||||
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||
</authentication-manager>
|
||||
|
||||
|
||||
<beans:bean id="requiredRoles" class="Array">
|
||||
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
@@ -44,7 +44,7 @@ public class AuthenticatedRequest implements Request {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("{");
|
||||
buf.append("Session ID: ").append(sessionId);
|
||||
buf.append("Token: ").append(token.getTimelyPrincipal().getName());
|
||||
buf.append("Token: ").append(token == null ? null : token.getTimelyPrincipal().getName());
|
||||
buf.append(", Request Headers: ").append(requestHeaders.toString());
|
||||
buf.append("}");
|
||||
return buf.toString();
|
||||
|
||||
@@ -20,7 +20,7 @@ import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||
|
||||
public class AuthCache {
|
||||
|
||||
private static Logger LOG = LoggerFactory.getLogger(AuthCache.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AuthCache.class);
|
||||
private static Cache<String, TimelyPrincipal> CACHE = null;
|
||||
|
||||
private static int sessionMaxAge = -1;
|
||||
@@ -39,7 +39,7 @@ public class AuthCache {
|
||||
sessionMaxAge = security.getSessionMaxAge();
|
||||
}
|
||||
|
||||
public static Cache<String, TimelyPrincipal> getCache() {
|
||||
private static Cache<String, TimelyPrincipal> getCache() {
|
||||
if (-1 == sessionMaxAge) {
|
||||
throw new IllegalStateException("Cache session max age not configured.");
|
||||
}
|
||||
@@ -49,10 +49,32 @@ public class AuthCache {
|
||||
return CACHE;
|
||||
}
|
||||
|
||||
public static boolean containsKey(String key) {
|
||||
return getCache().getIfPresent(key) != null;
|
||||
}
|
||||
|
||||
public static TimelyPrincipal get(String key) {
|
||||
return getCache().getIfPresent(key);
|
||||
}
|
||||
|
||||
public static void put(String key, TimelyPrincipal principal) {
|
||||
getCache().put(key, principal);
|
||||
}
|
||||
|
||||
public static void remove(String key) {
|
||||
getCache().invalidate(key);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
if (CACHE != null) {
|
||||
CACHE.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected static Collection<Authorizations> getAuthorizations(String entityName) {
|
||||
if (!StringUtils.isEmpty(entityName)) {
|
||||
List<Authorizations> authorizationsList = new ArrayList<>();
|
||||
TimelyPrincipal principal = CACHE.asMap().get(entityName);
|
||||
TimelyPrincipal principal = AuthCache.get(entityName);
|
||||
if (principal != null) {
|
||||
for (Collection<String> authCollection : principal.getAuthorizations()) {
|
||||
authorizationsList.add(AuthorizationsUtil.toAuthorizations(authCollection));
|
||||
|
||||
@@ -2,8 +2,14 @@ package timely.auth;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -20,21 +26,28 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
||||
import timely.api.request.AuthenticatedRequest;
|
||||
import timely.api.response.TimelyException;
|
||||
import timely.auth.util.AuthenticationUtils;
|
||||
import timely.auth.util.DnUtils;
|
||||
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||
import timely.netty.http.auth.TimelyUserDetails;
|
||||
|
||||
public class AuthenticationService {
|
||||
|
||||
private static Logger LOG = LoggerFactory.getLogger(AuthenticationService.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationService.class);
|
||||
private static ApplicationContext springContext = null;
|
||||
private static AuthenticationManager authManager = null;
|
||||
private static SubjectDnX509PrincipalExtractor x509 = null;
|
||||
private static ConcurrentHashMap<String, Object> fetchingEntity = new ConcurrentHashMap<>();
|
||||
private static ConcurrentHashMap<String, Object> fetchingPrincipal = new ConcurrentHashMap<>();
|
||||
private static Collection<String> requiredRoles;
|
||||
private static Collection<String> requiredAuths;
|
||||
|
||||
static {
|
||||
try {
|
||||
springContext = new ClassPathXmlApplicationContext("security.xml");
|
||||
authManager = (AuthenticationManager) springContext.getBean("authenticationManager");
|
||||
x509 = (SubjectDnX509PrincipalExtractor) springContext.getBean("x509PrincipalExtractor");
|
||||
authManager = springContext.getBean("authenticationManager", AuthenticationManager.class);
|
||||
x509 = springContext.getBean("x509PrincipalExtractor", SubjectDnX509PrincipalExtractor.class);
|
||||
requiredRoles = springContext.getBean("requiredRoles", List.class);
|
||||
requiredAuths = springContext.getBean("requiredAuths", List.class);
|
||||
} catch (BeansException e) {
|
||||
throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(),
|
||||
e.getRootCause());
|
||||
@@ -45,23 +58,54 @@ public class AuthenticationService {
|
||||
return authManager;
|
||||
}
|
||||
|
||||
public static TimelyAuthenticationToken authenticate(Authentication authentication) {
|
||||
return authenticate(authentication, null);
|
||||
public static TimelyPrincipal authenticate(Authentication authentication, String cacheName) {
|
||||
return authenticate(authentication, SubjectIssuerDNPair.of(cacheName), cacheName);
|
||||
}
|
||||
|
||||
public static TimelyAuthenticationToken authenticate(Authentication authentication, X509Certificate clientCert) {
|
||||
Authentication token = getAuthenticationManager().authenticate(authentication);
|
||||
return AuthenticationUtils.getTimelyAuthenticationToken(token, clientCert);
|
||||
public static TimelyPrincipal authenticate(Authentication authentication, SubjectIssuerDNPair pair, String entity) {
|
||||
|
||||
// only one thread should call the authenticationManager for a given entity
|
||||
TimelyPrincipal principal = AuthCache.get(entity);
|
||||
// once an entity is in the AuthCache, we can bypass all of this
|
||||
if (principal == null) {
|
||||
Object newObj = new Object();
|
||||
Object o = fetchingEntity.getOrDefault(entity, newObj);
|
||||
if (o == null) {
|
||||
o = newObj;
|
||||
}
|
||||
try {
|
||||
synchronized (o) {
|
||||
// if multiple threads are waiting on this synchronized, then the 2nd and all
|
||||
// others will get the principal here
|
||||
principal = AuthCache.get(entity);
|
||||
if (principal == null) {
|
||||
Authentication token = getAuthenticationManager().authenticate(authentication);
|
||||
principal = getTimelyPrincipal(token, pair);
|
||||
LOG.trace("Got entity principal for {} from AuthenticationManager", entity);
|
||||
AuthCache.put(entity, principal);
|
||||
} else {
|
||||
LOG.trace("Got entity principal for {} from AuthCache on 2nd attempt", entity);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
fetchingEntity.remove(entity);
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Got entity principal for {} from AuthCache on 1st attempt", entity);
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
|
||||
public static void enforceAccess(AuthenticatedRequest request) throws Exception {
|
||||
String sessionId = request.getSessionId();
|
||||
X509Certificate clientCert = request.getToken().getClientCert();
|
||||
TimelyPrincipal verifiedPrincipal = null;
|
||||
if (StringUtils.isBlank(sessionId) && clientCert == null) {
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
|
||||
"Anonymous access is disabled. User must either send a client certificate or log in");
|
||||
} else if (StringUtils.isNotBlank(sessionId)) {
|
||||
if (!AuthCache.getCache().asMap().containsKey(sessionId)) {
|
||||
verifiedPrincipal = AuthCache.get(sessionId);
|
||||
if (verifiedPrincipal == null) {
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
|
||||
"Unknown session id was submitted, log in again");
|
||||
}
|
||||
@@ -69,26 +113,62 @@ public class AuthenticationService {
|
||||
try {
|
||||
TimelyAuthenticationToken token = request.getToken();
|
||||
TimelyPrincipal requestPrincipal = token.getTimelyPrincipal();
|
||||
if (!AuthCache.getCache().asMap().containsKey(requestPrincipal.getName())) {
|
||||
List<TimelyUser> authenticatedTimelyUsers = new ArrayList<>();
|
||||
for (TimelyUser user : requestPrincipal.getProxiedUsers()) {
|
||||
SubjectIssuerDNPair p = user.getDn();
|
||||
TimelyAuthenticationToken entityToken = getAuthenticationToken(clientCert, p.subjectDN(),
|
||||
p.issuerDN());
|
||||
TimelyPrincipal entityPrincipal = AuthCache.getCache().asMap()
|
||||
.get(entityToken.getTimelyPrincipal().getName());
|
||||
if (entityPrincipal == null) {
|
||||
TimelyAuthenticationToken t = authenticate(entityToken, clientCert);
|
||||
entityPrincipal = t.getTimelyPrincipal();
|
||||
AuthCache.getCache().asMap().put(entityPrincipal.getName(), entityPrincipal);
|
||||
}
|
||||
authenticatedTimelyUsers.addAll(entityPrincipal.getProxiedUsers());
|
||||
String entity = requestPrincipal.getName();
|
||||
if (!AuthCache.containsKey(requestPrincipal.getName())) {
|
||||
Object newObj = new Object();
|
||||
Object o = fetchingPrincipal.getOrDefault(entity, newObj);
|
||||
if (o == null) {
|
||||
o = newObj;
|
||||
}
|
||||
TimelyPrincipal principal = new TimelyPrincipal(authenticatedTimelyUsers);
|
||||
AuthCache.getCache().asMap().put(principal.getName(), principal);
|
||||
LOG.debug("Authenticated user {} for request {} with authorizations {}", principal.getName(),
|
||||
request.toString(), principal.getAuthorizationsString());
|
||||
try {
|
||||
synchronized (o) {
|
||||
// check again if in the AuthCache
|
||||
if (!AuthCache.containsKey(entity)) {
|
||||
// first thread here, so authenticate all entities in requestPrincipal
|
||||
List<TimelyUser> authenticatedTimelyUsers = new ArrayList<>();
|
||||
for (TimelyUser user : requestPrincipal.getProxiedUsers()) {
|
||||
SubjectIssuerDNPair p = user.getDn();
|
||||
TimelyAuthenticationToken entityToken = getAuthenticationToken(clientCert,
|
||||
p.subjectDN(), p.issuerDN());
|
||||
TimelyPrincipal entityPrincipal = authenticate(entityToken, p,
|
||||
entityToken.getTimelyPrincipal().getName());
|
||||
authenticatedTimelyUsers.addAll(entityPrincipal.getProxiedUsers());
|
||||
}
|
||||
TimelyPrincipal principal = new TimelyPrincipal(authenticatedTimelyUsers);
|
||||
AuthCache.put(principal.getName(), principal);
|
||||
LOG.debug("Authenticated user {} for request {} with authorizations {}",
|
||||
principal.getName(), request.toString(), principal.getAuthorizationsString());
|
||||
} else {
|
||||
LOG.trace("Verified principal {} in AuthCache on 2nd attempt", entity);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
fetchingPrincipal.remove(entity);
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Verified principal {} in AuthCache on 1st attempt", entity);
|
||||
}
|
||||
|
||||
TimelyUser primaryUser = verifiedPrincipal.getPrimaryUser();
|
||||
if (requiredRoles != null) {
|
||||
if (!primaryUser.getRoles().containsAll(requiredRoles)) {
|
||||
Set<String> missingRoles = new TreeSet<>();
|
||||
missingRoles.addAll(requiredRoles);
|
||||
missingRoles.removeAll(primaryUser.getRoles());
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied",
|
||||
"User:" + primaryUser.getCommonName() + " is missing auth(s):" + missingRoles);
|
||||
}
|
||||
}
|
||||
if (requiredAuths != null) {
|
||||
if (!primaryUser.getAuths().containsAll(requiredAuths)) {
|
||||
Set<String> missingAuths = new TreeSet<>();
|
||||
missingAuths.addAll(requiredRoles);
|
||||
missingAuths.removeAll(primaryUser.getAuths());
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied",
|
||||
"User:" + primaryUser.getCommonName() + " is missing role(s):" + missingAuths);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "Access denied", e.getMessage());
|
||||
@@ -135,4 +215,21 @@ public class AuthenticationService {
|
||||
}
|
||||
return authenticationToken;
|
||||
}
|
||||
|
||||
private static TimelyPrincipal getTimelyPrincipal(Authentication authentication,
|
||||
SubjectIssuerDNPair subjectIssuerDNPair) {
|
||||
String subjectDn = subjectIssuerDNPair.subjectDN();
|
||||
TimelyUser.UserType userType = DnUtils.isServerDN(subjectDn) ? TimelyUser.UserType.SERVER
|
||||
: TimelyUser.UserType.USER;
|
||||
Collection<String> auths = authentication.getAuthorities().stream().map(a -> a.getAuthority())
|
||||
.collect(Collectors.toList());
|
||||
Object o = authentication.getPrincipal();
|
||||
Collection<String> roles = null;
|
||||
if (o instanceof TimelyUserDetails) {
|
||||
roles = ((TimelyUserDetails) o).getRoles();
|
||||
}
|
||||
TimelyUser timelyUser = new TimelyUser(subjectIssuerDNPair, userType, auths, roles, null,
|
||||
System.currentTimeMillis());
|
||||
return new TimelyPrincipal(Arrays.asList(timelyUser));
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public class TimelyPrincipal implements Principal, Serializable {
|
||||
TimelyUser first = proxiedUsers.stream().findFirst().orElse(null);
|
||||
this.primaryUser = proxiedUsers.stream().filter(u -> u.getUserType() == TimelyUser.UserType.USER).findFirst()
|
||||
.orElse(first);
|
||||
this.proxiedUsers.add(first);
|
||||
this.proxiedUsers.add(primaryUser);
|
||||
this.proxiedUsers
|
||||
.addAll(proxiedUsers.stream().filter(u -> !u.equals(primaryUser)).collect(Collectors.toList()));
|
||||
this.username = this.proxiedUsers.stream().map(TimelyUser::getName).collect(Collectors.joining(" -> "));
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import timely.auth.util.ProxiedEntityUtils;
|
||||
import timely.auth.util.DnUtils;
|
||||
|
||||
/**
|
||||
* A user of a TIMELY service. Typically, one or more of these users (a chain
|
||||
@@ -38,11 +38,28 @@ public class TimelyUser implements Serializable {
|
||||
private final long creationTime;
|
||||
private final long expirationTime;
|
||||
|
||||
/*
|
||||
* For Spring configuration
|
||||
*/
|
||||
public TimelyUser(String subjectDn, Collection<String> auths, Collection<String> roles) {
|
||||
this.name = subjectDn;
|
||||
this.commonName = DnUtils.getCommonName(subjectDn);
|
||||
this.dn = SubjectIssuerDNPair.of(subjectDn);
|
||||
this.userType = DnUtils.isServerDN(subjectDn) ? UserType.SERVER : UserType.USER;
|
||||
this.auths = auths == null ? Collections.emptyList() : new LinkedHashSet<>(auths);
|
||||
this.unmodifiableAuths = Collections.unmodifiableCollection(this.auths);
|
||||
this.roles = roles == null ? Collections.emptyList() : new LinkedHashSet<>(roles);
|
||||
this.unmodifiableRoles = Collections.unmodifiableCollection(this.roles);
|
||||
this.roleToAuthMapping = null;
|
||||
this.creationTime = System.currentTimeMillis();
|
||||
this.expirationTime = -1L;
|
||||
}
|
||||
|
||||
/*
|
||||
* For testing
|
||||
*/
|
||||
public TimelyUser(String subjectDn, Collection<String> auths) {
|
||||
this(SubjectIssuerDNPair.of(subjectDn), UserType.USER, auths, null, null, System.currentTimeMillis(), -1);
|
||||
this(SubjectIssuerDNPair.of(subjectDn), UserType.USER, auths, null, null, System.currentTimeMillis(), -1L);
|
||||
}
|
||||
|
||||
public TimelyUser(SubjectIssuerDNPair dn, UserType userType, Collection<String> auths, Collection<String> roles,
|
||||
@@ -58,7 +75,7 @@ public class TimelyUser implements Serializable {
|
||||
@JsonProperty(value = "creationTime", defaultValue = "-1L") long creationTime,
|
||||
@JsonProperty(value = "expirationTime", defaultValue = "-1L") long expirationTime) {
|
||||
this.name = dn.toString();
|
||||
this.commonName = ProxiedEntityUtils.getCommonName(dn.subjectDN());
|
||||
this.commonName = DnUtils.getCommonName(dn.subjectDN());
|
||||
this.dn = dn;
|
||||
this.userType = userType;
|
||||
this.auths = auths == null ? Collections.emptyList() : new LinkedHashSet<>(auths);
|
||||
|
||||
@@ -3,7 +3,6 @@ package timely.auth;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -11,18 +10,20 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import timely.netty.http.auth.TimelyUserDetails;
|
||||
|
||||
public class UserDetailsService implements
|
||||
org.springframework.security.core.userdetails.AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
|
||||
|
||||
private HashMap<String, List<String>> users;
|
||||
private HashMap<String, TimelyUser> users;
|
||||
|
||||
public Map<String, List<String>> getUsers() {
|
||||
public Map<String, TimelyUser> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(LinkedHashMap<String, List<String>> users) {
|
||||
this.users = new HashMap<>(users);
|
||||
public void setUsers(List<TimelyUser> users) {
|
||||
this.users = new HashMap<>();
|
||||
users.forEach(u -> this.users.put(u.getName(), u));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,25 +31,32 @@ public class UserDetailsService implements
|
||||
throws UsernameNotFoundException {
|
||||
// Determine if the user is allowed to access the system, if not throw
|
||||
// UsernameNotFoundException
|
||||
String username = token.getName().toString();
|
||||
String username = token.getName();
|
||||
if (!users.containsKey(username)) {
|
||||
throw new UsernameNotFoundException(username + " not configured.");
|
||||
}
|
||||
// If allowed, populate the user details object with the authorities for
|
||||
// the user.
|
||||
return new UserDetails() {
|
||||
return new TimelyUserDetails() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Collection<SimpleGrantedAuthority> getAuthorities() {
|
||||
final Collection<SimpleGrantedAuthority> auths = new ArrayList<>();
|
||||
users.get(username).forEach(a -> {
|
||||
users.get(username).getAuths().forEach(a -> {
|
||||
auths.add(new SimpleGrantedAuthority(a));
|
||||
});
|
||||
return auths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getRoles() {
|
||||
final Collection<String> roles = new ArrayList<>();
|
||||
roles.addAll(users.get(username).getRoles());
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "";
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package timely.auth.util;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import timely.auth.SubjectIssuerDNPair;
|
||||
import timely.auth.TimelyPrincipal;
|
||||
import timely.auth.TimelyUser;
|
||||
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||
|
||||
public class AuthenticationUtils {
|
||||
|
||||
public static Collection<String> getAuthCollection(Authentication authentication) {
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
return authorities.stream().map(a -> a.getAuthority()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static TimelyAuthenticationToken getTimelyAuthenticationToken(Authentication authentication,
|
||||
X509Certificate clientCert) {
|
||||
TimelyAuthenticationToken timelyAuthenticationToken = null;
|
||||
if (authentication instanceof TimelyAuthenticationToken) {
|
||||
return (TimelyAuthenticationToken) authentication;
|
||||
} else {
|
||||
if (clientCert == null) {
|
||||
timelyAuthenticationToken = new TimelyAuthenticationToken(authentication.getName(), null,
|
||||
HashMultimap.create());
|
||||
TimelyUser timelyUser = new TimelyUser(SubjectIssuerDNPair.of(authentication.getName()),
|
||||
TimelyUser.UserType.USER, AuthenticationUtils.getAuthCollection(authentication), null, null,
|
||||
System.currentTimeMillis());
|
||||
timelyAuthenticationToken.setTimelyPrincipal(new TimelyPrincipal(timelyUser));
|
||||
} else {
|
||||
timelyAuthenticationToken = new TimelyAuthenticationToken(authentication.getName(), clientCert,
|
||||
HashMultimap.create());
|
||||
TimelyUser.UserType userType = DnUtils.isServerDN(clientCert.getSubjectDN().getName())
|
||||
? TimelyUser.UserType.SERVER
|
||||
: TimelyUser.UserType.USER;
|
||||
TimelyUser timelyUser = new TimelyUser(SubjectIssuerDNPair.of(authentication.getName()), userType,
|
||||
AuthenticationUtils.getAuthCollection(authentication), null, null, System.currentTimeMillis());
|
||||
timelyAuthenticationToken.setTimelyPrincipal(new TimelyPrincipal(timelyUser));
|
||||
}
|
||||
}
|
||||
return timelyAuthenticationToken;
|
||||
}
|
||||
}
|
||||
@@ -152,10 +152,10 @@ public class ProxiedEntityUtils {
|
||||
if (clientCert != null) {
|
||||
List<String> proxiedEntitiesList = new ArrayList<>();
|
||||
if (headers.containsKey(TimelyAuthenticationToken.PROXIED_ENTITIES_HEADER)) {
|
||||
String proxiesEntities = HttpHeaderUtils.getSingleHeader(headers,
|
||||
String proxiedEntities = HttpHeaderUtils.getSingleHeader(headers,
|
||||
TimelyAuthenticationToken.PROXIED_ENTITIES_HEADER, false);
|
||||
headers.removeAll(TimelyAuthenticationToken.PROXIED_ENTITIES_HEADER);
|
||||
String[] preExistingProxiedEntities = ProxiedEntityUtils.splitProxiedDNs(proxiesEntities, false);
|
||||
String[] preExistingProxiedEntities = ProxiedEntityUtils.splitProxiedDNs(proxiedEntities, false);
|
||||
if (preExistingProxiedEntities.length > 0) {
|
||||
proxiedEntitiesList.addAll(Arrays.asList(preExistingProxiedEntities));
|
||||
}
|
||||
@@ -169,10 +169,10 @@ public class ProxiedEntityUtils {
|
||||
|
||||
List<String> proxiedIssuersList = new ArrayList<>();
|
||||
if (headers.containsKey(TimelyAuthenticationToken.PROXIED_ISSUERS_HEADER)) {
|
||||
String proxiesIssuers = HttpHeaderUtils.getSingleHeader(headers,
|
||||
String proxiedIssuers = HttpHeaderUtils.getSingleHeader(headers,
|
||||
TimelyAuthenticationToken.PROXIED_ISSUERS_HEADER, false);
|
||||
headers.removeAll(TimelyAuthenticationToken.PROXIED_ISSUERS_HEADER);
|
||||
String[] preExistingProxiedIssuers = ProxiedEntityUtils.splitProxiedDNs(proxiesIssuers, false);
|
||||
String[] preExistingProxiedIssuers = ProxiedEntityUtils.splitProxiedDNs(proxiedIssuers, false);
|
||||
if (preExistingProxiedIssuers.length > 0) {
|
||||
proxiedEntitiesList.addAll(Arrays.asList(preExistingProxiedIssuers));
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
|
||||
if (StringUtils.isNotBlank(sessionId)) {
|
||||
TimelyPrincipal principal;
|
||||
((AuthenticatedRequest) request).setSessionId(sessionId);
|
||||
if (AuthCache.getCache().asMap().containsKey(sessionId)) {
|
||||
principal = AuthCache.getCache().asMap().get(sessionId);
|
||||
if (AuthCache.containsKey(sessionId)) {
|
||||
principal = AuthCache.get(sessionId);
|
||||
} else {
|
||||
principal = TimelyPrincipal.anonymousPrincipal();
|
||||
}
|
||||
|
||||
@@ -20,13 +20,12 @@ public class BasicAuthLoginRequestHandler extends TimelyLoginRequestHandler<Basi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, BasicAuthLoginRequest msg)
|
||||
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, BasicAuthLoginRequest msg, String sessionId)
|
||||
throws AuthenticationException {
|
||||
// Perform the login process using username/password
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(msg.getUsername(),
|
||||
msg.getPassword());
|
||||
TimelyAuthenticationToken timelyToken = AuthenticationService.authenticate(token);
|
||||
TimelyPrincipal principal = timelyToken.getTimelyPrincipal();
|
||||
TimelyPrincipal principal = AuthenticationService.authenticate(token, sessionId);
|
||||
|
||||
LOG.debug("Authenticated user {} with authorizations {}", principal.getPrimaryUser().getDn().subjectDN(),
|
||||
principal.getAuthorizationsString());
|
||||
|
||||
@@ -22,21 +22,19 @@ public class TimelyAuthenticationToken extends PreAuthenticatedAuthenticationTok
|
||||
public static final String PROXIED_ISSUERS_HEADER = "X-ProxiedIssuersChain";
|
||||
|
||||
private Multimap<String, String> httpHeaders = null;
|
||||
private List<SubjectIssuerDNPair> entities = new ArrayList<>();
|
||||
private String completeProxyName;
|
||||
private X509Certificate clientCert;
|
||||
private TimelyPrincipal timelyPrincipal;
|
||||
|
||||
public TimelyAuthenticationToken(String subjectDN, X509Certificate clientCert,
|
||||
public TimelyAuthenticationToken(String subjectDn, X509Certificate clientCert,
|
||||
Multimap<String, String> httpHeaders) {
|
||||
super(subjectDN, clientCert);
|
||||
super(subjectDn, clientCert);
|
||||
if (httpHeaders == null) {
|
||||
this.httpHeaders = HashMultimap.create();
|
||||
} else {
|
||||
this.httpHeaders = httpHeaders;
|
||||
}
|
||||
this.clientCert = clientCert;
|
||||
String issuerDn = clientCert == null ? null : clientCert.getSubjectDN().getName();
|
||||
String issuerDn = clientCert == null ? null : clientCert.getIssuerDN().getName();
|
||||
String proxiedEntities;
|
||||
String proxiedIssuers;
|
||||
try {
|
||||
@@ -51,7 +49,7 @@ public class TimelyAuthenticationToken extends PreAuthenticatedAuthenticationTok
|
||||
throw new IllegalArgumentException(
|
||||
PROXIED_ENTITIES_HEADER + " supplied, but missing " + PROXIED_ISSUERS_HEADER);
|
||||
}
|
||||
List<SubjectIssuerDNPair> entities = extractEntities(subjectDN, issuerDn, proxiedEntities, proxiedIssuers);
|
||||
List<SubjectIssuerDNPair> entities = extractEntities(subjectDn, issuerDn, proxiedEntities, proxiedIssuers);
|
||||
long now = System.currentTimeMillis();
|
||||
List<TimelyUser> timelyUsers = new ArrayList<>();
|
||||
for (SubjectIssuerDNPair ent : entities) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.auth.TimelyPrincipal;
|
||||
import timely.configuration.Http;
|
||||
import timely.configuration.Security;
|
||||
@@ -38,10 +37,9 @@ public abstract class TimelyLoginRequestHandler<T> extends SimpleChannelInboundH
|
||||
protected final void channelRead0(ChannelHandlerContext ctx, T loginRequest) throws Exception {
|
||||
try {
|
||||
LOG.trace("Authenticating {}", loginRequest);
|
||||
TimelyPrincipal principal = authenticate(ctx, loginRequest);
|
||||
String sessionId = UUID.randomUUID().toString();
|
||||
AuthCache.getCache().put(sessionId, principal);
|
||||
LOG.debug("Authenticated new sessionId {} for user {}", sessionId,
|
||||
TimelyPrincipal principal = authenticate(ctx, loginRequest, sessionId);
|
||||
LOG.trace("Authenticated new sessionId {} for user {}", sessionId,
|
||||
principal.getPrimaryUser().getDn().subjectDN());
|
||||
String sessionIdEncoded = URLEncoder.encode(sessionId, StandardCharsets.UTF_8.name());
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
@@ -66,5 +64,6 @@ public abstract class TimelyLoginRequestHandler<T> extends SimpleChannelInboundH
|
||||
|
||||
}
|
||||
|
||||
protected abstract TimelyPrincipal authenticate(ChannelHandlerContext ctx, T loginRequest) throws Exception;
|
||||
protected abstract TimelyPrincipal authenticate(ChannelHandlerContext ctx, T loginRequest, String entity)
|
||||
throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package timely.netty.http.auth;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public interface TimelyUserDetails extends UserDetails {
|
||||
|
||||
Collection<String> getRoles();
|
||||
}
|
||||
@@ -9,34 +9,36 @@ import org.slf4j.LoggerFactory;
|
||||
import timely.api.request.auth.X509LoginRequest;
|
||||
import timely.api.response.TimelyException;
|
||||
import timely.auth.AuthenticationService;
|
||||
import timely.auth.SubjectIssuerDNPair;
|
||||
import timely.auth.TimelyPrincipal;
|
||||
import timely.configuration.Http;
|
||||
import timely.configuration.Security;
|
||||
|
||||
public class X509LoginRequestHandler extends TimelyLoginRequestHandler<X509LoginRequest> {
|
||||
|
||||
private static Logger LOG = LoggerFactory.getLogger(X509LoginRequestHandler.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(X509LoginRequestHandler.class);
|
||||
|
||||
public X509LoginRequestHandler(Security security, Http http) {
|
||||
super(security, http);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, X509LoginRequest loginRequest) throws Exception {
|
||||
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, X509LoginRequest loginRequest, String sessionId)
|
||||
throws Exception {
|
||||
// If we are operating in 2 way SSL, then get the subjectDN from the
|
||||
// client certificate and perform the login process.
|
||||
TimelyPrincipal principal = null;
|
||||
try {
|
||||
X509Certificate clientCert = AuthenticationService.getClientCertificate(ctx);
|
||||
|
||||
if (clientCert == null) {
|
||||
throw new IllegalArgumentException("No client certificate found");
|
||||
} else {
|
||||
// login endpoint is for direct certificate only, not for proxying
|
||||
TimelyAuthenticationToken token = AuthenticationService.getAuthenticationToken(clientCert,
|
||||
clientCert.getSubjectDN().getName(), clientCert.getIssuerDN().getName());
|
||||
TimelyAuthenticationToken t = AuthenticationService.authenticate(token, clientCert);
|
||||
principal = t.getTimelyPrincipal();
|
||||
SubjectIssuerDNPair pair = SubjectIssuerDNPair.of(clientCert.getSubjectDN().getName(),
|
||||
clientCert.getIssuerDN().getName());
|
||||
principal = AuthenticationService.authenticate(token, pair, sessionId);
|
||||
LOG.debug("Authenticated user {} with authorizations {}",
|
||||
principal.getPrimaryUser().getDn().subjectDN(), principal.getAuthorizationsString());
|
||||
}
|
||||
|
||||
@@ -50,11 +50,9 @@ public class WebSocketRequestDecoder extends MessageToMessageDecoder<WebSocketFr
|
||||
String content = frame.text();
|
||||
WebSocketRequest request = JsonUtil.getObjectMapper().readValue(content, WebSocketRequest.class);
|
||||
LOG.trace("Received WS request {}", content);
|
||||
|
||||
String sessionId = ctx.channel().attr(SubscriptionRegistry.SESSION_ID_ATTR).get();
|
||||
Multimap<String, String> headers = ctx.channel().attr(HTTP_HEADERS_ATTR).get();
|
||||
X509Certificate clientCert = ctx.channel().attr(CLIENT_CERT_ATTR).get();
|
||||
|
||||
if (request instanceof AuthenticatedRequest) {
|
||||
if (headers != null) {
|
||||
((AuthenticatedRequest) request).addHeaders(headers);
|
||||
@@ -63,8 +61,8 @@ public class WebSocketRequestDecoder extends MessageToMessageDecoder<WebSocketFr
|
||||
if (StringUtils.isNotBlank(sessionId)) {
|
||||
TimelyPrincipal principal;
|
||||
((AuthenticatedRequest) request).setSessionId(sessionId);
|
||||
if (AuthCache.getCache().asMap().containsKey(sessionId)) {
|
||||
principal = AuthCache.getCache().asMap().get(sessionId);
|
||||
if (AuthCache.containsKey(sessionId)) {
|
||||
principal = AuthCache.get(sessionId);
|
||||
} else {
|
||||
principal = TimelyPrincipal.anonymousPrincipal();
|
||||
}
|
||||
|
||||
@@ -968,7 +968,7 @@ public class DataStoreImpl implements DataStore {
|
||||
throw new IllegalArgumentException("metric name must be specified");
|
||||
}
|
||||
Collection<Authorizations> auths = getSessionAuthorizations(request);
|
||||
LOG.debug("Creating metric scanner for session: {} with auths: {}", request.getSessionId(), auths);
|
||||
LOG.debug("Creating metric scanner for [{}] with auths: {}", request.getUserName(), auths);
|
||||
Scanner s = ScannerHelper.createScanner(connector, this.metricsTable, auths);
|
||||
if (tags == null) {
|
||||
tags = new LinkedHashMap<>();
|
||||
|
||||
@@ -650,7 +650,7 @@ public class DataStoreCache {
|
||||
Collection<Authorizations> minimizedAuths = AuthorizationsUtil.minimize(authorizations);
|
||||
Collection<VisibilityFilter> visibilityFilters = new ArrayList<>();
|
||||
for (Authorizations a : minimizedAuths) {
|
||||
((ArrayList<VisibilityFilter>) visibilityFilters).add(new VisibilityFilter(a));
|
||||
visibilityFilters.add(new VisibilityFilter(a));
|
||||
}
|
||||
DataStoreCacheIterator itr = new DataStoreCacheIterator(this, visibilityFilters, subQuery, begin, end);
|
||||
try {
|
||||
|
||||
@@ -121,12 +121,12 @@ public class MetricScanner extends Thread implements UncaughtExceptionHandler {
|
||||
// cached
|
||||
ranges.addAll(
|
||||
this.store.getQueryRanges(metric, this.beginTime, oldestTsForMetric - 1, colFamValues));
|
||||
metricsFromCache = cache.getMetricsFromCache(request, metric, tags, oldestTsForMetric,
|
||||
metricsFromCache = cache.getMetricsFromCache(this.request, metric, tags, oldestTsForMetric,
|
||||
endTimeStamp);
|
||||
LOG.debug("[{}] MetricScanner request partially fulfilled from cache: {} metrics",
|
||||
subscriptionId, metricsFromCache.size());
|
||||
} else {
|
||||
metricsFromCache = cache.getMetricsFromCache(request, metric, tags, this.beginTime,
|
||||
metricsFromCache = cache.getMetricsFromCache(this.request, metric, tags, this.beginTime,
|
||||
endTimeStamp);
|
||||
LOG.debug("[{}] Websocket request completely fulfilled from cache: {} metrics [{}]",
|
||||
subscriptionId, metricsFromCache.size());
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
npe.ou.entries=iamnotaperson,npe,stillnotaperson
|
||||
subject.dn.pattern=(?:^|,)\\s*OU\\s*My Department\\s*(?:,|$)
|
||||
subject.dn.pattern=(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)
|
||||
@@ -7,13 +7,13 @@ import java.util.UUID;
|
||||
|
||||
import org.apache.accumulo.core.security.Authorizations;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import timely.configuration.Configuration;
|
||||
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||
import timely.test.TestConfiguration;
|
||||
|
||||
public class AuthCacheTest {
|
||||
@@ -28,12 +28,16 @@ public class AuthCacheTest {
|
||||
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void after() {
|
||||
AuthCache.resetSessionMaxAge();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||
TimelyAuthenticationToken auth = AuthenticationService.authenticate(token);
|
||||
AuthCache.getCache().put(cookie, auth.getTimelyPrincipal());
|
||||
AuthenticationService.authenticate(token, cookie);
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -2,31 +2,54 @@ package timely.auth;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import timely.configuration.Configuration;
|
||||
import timely.test.TestConfiguration;
|
||||
|
||||
public class AuthenticationServiceTest {
|
||||
|
||||
private static Configuration config;
|
||||
|
||||
@BeforeClass
|
||||
public static void before() {
|
||||
config = TestConfiguration.createMinimalConfigurationForTest();
|
||||
config.getSecurity().setSessionMaxAge(Integer.MAX_VALUE);
|
||||
AuthCache.resetSessionMaxAge();
|
||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
AuthCache.clear();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void after() {
|
||||
AuthCache.resetSessionMaxAge();
|
||||
}
|
||||
|
||||
@Test(expected = BadCredentialsException.class)
|
||||
public void testBasicAuthenticationFailure() {
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test2");
|
||||
AuthenticationService.authenticate(token);
|
||||
AuthenticationService.authenticate(token, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicAuthenticationLogin() {
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||
Authentication auth = AuthenticationService.authenticate(token);
|
||||
Collection<? extends GrantedAuthority> authorizations = auth.getAuthorities();
|
||||
TimelyPrincipal principal = AuthenticationService.authenticate(token, "test");
|
||||
Collection<? extends Collection<String>> authorizations = principal.getAuthorizations();
|
||||
authorizations.forEach(a -> {
|
||||
Assert.assertTrue(
|
||||
a.getAuthority().equals("A") || a.getAuthority().equals("B") || a.getAuthority().equals("C"));
|
||||
Assert.assertTrue(a.contains("A") || a.contains("B") || a.contains("C"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,11 +57,10 @@ public class AuthenticationServiceTest {
|
||||
public void testX509AuthenticationLogin() {
|
||||
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken("CN=example.com",
|
||||
"doesn't matter what I put here");
|
||||
Authentication auth = AuthenticationService.authenticate(token);
|
||||
Collection<? extends GrantedAuthority> authorizations = auth.getAuthorities();
|
||||
TimelyPrincipal principal = AuthenticationService.authenticate(token, "test");
|
||||
Collection<? extends Collection<String>> authorizations = principal.getAuthorizations();
|
||||
authorizations.forEach(a -> {
|
||||
Assert.assertTrue(
|
||||
a.getAuthority().equals("D") || a.getAuthority().equals("E") || a.getAuthority().equals("F"));
|
||||
Assert.assertTrue(a.contains("D") || a.contains("E") || a.contains("F"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,11 +68,10 @@ public class AuthenticationServiceTest {
|
||||
public void testX509AuthenticationLoginFailed() {
|
||||
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken("CN=bad.example.com",
|
||||
"doesn't matter what I put here");
|
||||
Authentication auth = AuthenticationService.authenticate(token);
|
||||
Collection<? extends GrantedAuthority> authorizations = auth.getAuthorities();
|
||||
TimelyPrincipal principal = AuthenticationService.authenticate(token, "test");
|
||||
Collection<? extends Collection<String>> authorizations = principal.getAuthorizations();
|
||||
authorizations.forEach(a -> {
|
||||
Assert.assertTrue(
|
||||
a.getAuthority().equals("D") || a.getAuthority().equals("E") || a.getAuthority().equals("F"));
|
||||
Assert.assertTrue(a.contains("D") || a.contains("E") || a.contains("F"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package timely.auth;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ServiceConfigurationError;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
public class UserDetailsServiceTest {
|
||||
|
||||
private static ApplicationContext springContext = null;
|
||||
private static UserDetailsService userDetailsService = null;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupClass() {
|
||||
try {
|
||||
springContext = new ClassPathXmlApplicationContext("security.xml");
|
||||
userDetailsService = (UserDetailsService) springContext.getBean("timelyUserDetailsService");
|
||||
} catch (BeansException e) {
|
||||
throw new ServiceConfigurationError("Error initializing from spring: " + e.getMessage(), e.getRootCause());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuths() {
|
||||
TimelyUser u = userDetailsService.getUsers().get("CN=example.com");
|
||||
Assert.assertNotNull("TimelyUser not found", u);
|
||||
Assert.assertEquals("3 auths expected", 3, u.getAuths().size());
|
||||
Assert.assertTrue("Unexpected auths found " + u.getAuths(),
|
||||
u.getAuths().containsAll(Arrays.asList("D", "E", "F")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles() {
|
||||
TimelyUser u = userDetailsService.getUsers().get("CN=example.com");
|
||||
Assert.assertNotNull("TimelyUser not found", u);
|
||||
Assert.assertEquals("3 roles expected", 3, u.getRoles().size());
|
||||
Assert.assertTrue("Unexpected roles found " + u.getRoles(),
|
||||
u.getRoles().containsAll(Arrays.asList("G", "H", "I")));
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ import timely.configuration.Configuration;
|
||||
import timely.model.Metric;
|
||||
import timely.model.Tag;
|
||||
import timely.netty.Constants;
|
||||
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||
import timely.test.TestConfiguration;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
@@ -73,8 +72,7 @@ public class HttpRequestDecoderTest {
|
||||
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||
TimelyAuthenticationToken auth = AuthenticationService.authenticate(token);
|
||||
AuthCache.getCache().put(cookie, auth.getTimelyPrincipal());
|
||||
AuthenticationService.authenticate(token, cookie);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
||||
@@ -57,6 +57,7 @@ public class BasicAuthLoginRequestHandlerTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
AuthCache.clear();
|
||||
results.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import timely.api.request.timeseries.SuggestRequest;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.auth.TimelyPrincipal;
|
||||
import timely.auth.TimelyUser;
|
||||
import timely.auth.util.AuthenticationUtils;
|
||||
import timely.configuration.Configuration;
|
||||
import timely.model.Metric;
|
||||
import timely.subscription.SubscriptionRegistry;
|
||||
@@ -67,8 +66,7 @@ public class WebSocketRequestDecoderTest {
|
||||
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
||||
AuthCache.setSessionMaxAge(config.getSecurity());
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
|
||||
AuthCache.getCache().put(cookie,
|
||||
new TimelyPrincipal(new TimelyUser("ANONYMOUS", AuthenticationUtils.getAuthCollection(token))));
|
||||
AuthCache.put(cookie, new TimelyPrincipal(TimelyUser.ANONYMOUS_USER));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.configuration.Configuration;
|
||||
import timely.store.MetricAgeOffIterator;
|
||||
import timely.test.TestConfiguration;
|
||||
@@ -83,4 +84,8 @@ public class MacITBase {
|
||||
conf.setMetricAgeOffDays(ageOffSettings);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initializeAuthCache() {
|
||||
AuthCache.clear();
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,7 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
AuthCache.clear();
|
||||
Connector con = mac.getConnector("root", "secret");
|
||||
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ import timely.auth.TimelyPrincipal;
|
||||
import timely.model.Metric;
|
||||
import timely.model.Tag;
|
||||
import timely.netty.Constants;
|
||||
import timely.netty.http.auth.TimelyAuthenticationToken;
|
||||
import timely.test.IntegrationTest;
|
||||
import timely.test.TestConfiguration;
|
||||
import timely.test.integration.OneWaySSLBase;
|
||||
@@ -196,7 +195,7 @@ public class WebSocketIT extends OneWaySSLBase {
|
||||
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
|
||||
|
||||
this.sessionId = UUID.randomUUID().toString();
|
||||
AuthCache.getCache().put(sessionId, TimelyPrincipal.anonymousPrincipal());
|
||||
AuthCache.put(sessionId, TimelyPrincipal.anonymousPrincipal());
|
||||
group = new NioEventLoopGroup();
|
||||
SslContext ssl = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
|
||||
|
||||
@@ -505,9 +504,8 @@ public class WebSocketIT extends OneWaySSLBase {
|
||||
@Test
|
||||
public void testSubscriptionWorkflowWithEndTimeAndVisibilities() throws Exception {
|
||||
|
||||
TimelyAuthenticationToken auth = AuthenticationService.authenticate(token);
|
||||
TimelyPrincipal principal = auth.getTimelyPrincipal();
|
||||
AuthCache.getCache().put(sessionId, principal);
|
||||
AuthCache.remove(sessionId);
|
||||
AuthenticationService.authenticate(token, sessionId);
|
||||
|
||||
try {
|
||||
final String subscriptionId = "1234";
|
||||
@@ -585,7 +583,7 @@ public class WebSocketIT extends OneWaySSLBase {
|
||||
close.setSubscriptionId(subscriptionId);
|
||||
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(close)));
|
||||
} finally {
|
||||
AuthCache.getCache().asMap().remove(sessionId, principal);
|
||||
AuthCache.remove(sessionId);
|
||||
ch.close().sync();
|
||||
s.shutdown();
|
||||
group.shutdownGracefully();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
npe.ou.entries=iamnotaperson,npe,stillnotaperson
|
||||
subject.dn.pattern=(?:^|,)\\s*OU\\s*My Department\\s*(?:,|$)
|
||||
subject.dn.pattern=(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)
|
||||
@@ -11,21 +11,33 @@
|
||||
|
||||
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
|
||||
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
|
||||
</beans:bean>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:map key-type="java.lang.String">
|
||||
<beans:entry key="CN=example.com">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:entry>
|
||||
</util:map>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:list>
|
||||
<beans:bean class="timely.auth.TimelyUser">
|
||||
<beans:constructor-arg name="subjectDn">
|
||||
<beans:value>CN=example.com</beans:value>
|
||||
</beans:constructor-arg>
|
||||
<beans:constructor-arg name="auths">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:constructor-arg>
|
||||
<beans:constructor-arg name="roles">
|
||||
<util:list>
|
||||
<beans:value>G</beans:value>
|
||||
<beans:value>H</beans:value>
|
||||
<beans:value>I</beans:value>
|
||||
</util:list>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
</util:list>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
|
||||
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
|
||||
|
||||
Reference in new issue
Block a user