Compare commits

..
2 Commits
Author SHA1 Message Date
Bill Oley 142f5c8dfb More work 2019-02-04 15:39:03 -05:00
Bill Oley 46a424bcce Work in progress 2019-02-01 17:08:43 -05:00
42 changed files with 227 additions and 465 deletions

No files matched your search

+14 -26
View File
@@ -11,33 +11,21 @@
<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: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="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="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
@@ -8,8 +8,7 @@ public class ClientSsl {
private String trustStoreFile;
private String trustStoreType;
private String trustStorePassword;
private boolean useClientCert = true;
private boolean hostVerificationEnabled = true;
private boolean twoWaySsl;
public String getKeyFile() {
return keyFile;
@@ -59,19 +58,7 @@ public class ClientSsl {
this.trustStorePassword = trustStorePassword;
}
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;
public boolean isTwoWaySsl() {
return twoWaySsl;
}
}
@@ -13,19 +13,18 @@ import timely.balancer.connection.TimelyBalancedHost;
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
private final SSLContext sslContext;
private final BalancerConfiguration balancerConfig;
private boolean twoWaySsl;
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,
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(),
balancerConfig.getSecurity().getClientSsl().isUseClientCert()));
return new DefaultPooledObject<>(
timely.client.http.HttpClient.get(this.sslContext, cookieJar, false, twoWaySsl));
}
@Override
@@ -12,11 +12,15 @@ 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.balancerConfig = balancerConfig;
this.sslContext = sslContext;
this.doLogin = balancerConfig.isLoginRequired();
this.balancerConfig = balancerConfig;
this.twoWaySsl = balancerConfig.getSecurity().getClientSsl().isTwoWaySsl();
}
@Override
@@ -24,9 +28,7 @@ public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedH
int bufferSize = balancerConfig.getWebsocket().getSubscriptionBatchSize() * 500;
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(),
balancerConfig.isLoginRequired(), "", "",
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(), bufferSize);
k.getWsPort(), twoWaySsl, doLogin, "", "", false, bufferSize);
return new DefaultPooledObject<>(client);
}
@@ -9,7 +9,6 @@ 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;
@@ -41,20 +40,18 @@ public class WsClientHandler extends ClientHandler {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
Multimap<String, String> requestHeaders = token.getHttpHeaders();
if (token.getClientCert() != null) {
Multimap<String, String> proxyRequestHeaders = HashMultimap.create();
ProxiedEntityUtils.addProxyHeaders(proxyRequestHeaders, token.getClientCert());
for (String s : proxyRequestHeaders.keySet()) {
headers.put(s, new ArrayList(proxyRequestHeaders.get(s)));
}
ProxiedEntityUtils.addProxyHeaders(requestHeaders, token.getClientCert());
}
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)));
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));
}
headers.put(s, valueList);
}
}
@@ -57,9 +57,9 @@ public class HttpClient {
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
if (clientAuth) {
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl,
SSLConnectionSocketFactory sslCF = new SSLConnectionSocketFactory(ssl,
new String[] { "TLSv1.1", "TLSv1.2" }, null, hostnameVerifier);
builder.setSSLSocketFactory(sslCSF);
builder.setSSLSocketFactory(sslCF);
}
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 doLogin;
private final boolean clientAuth;
private final boolean doLogin;
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', interpolate=True, fill=None):
def resample(df, sample, how='mean'):
dataFrame = pandas.DataFrame(df, copy=True)
if dataFrame is not None:
@@ -16,15 +16,14 @@ 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, client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
request_timeout=WebSocketClient.DEFAULT_REQUEST_TIMEOUT):
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,
client_cert=client_cert, client_key=client_key, ca_certs=ca_certs, validate_cert=validate_cert)
WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout)
def _on_message(self, msg):
# implement in subclass
@@ -73,8 +72,7 @@ class TimelyMetric(TimelyWebSocketClient):
client = None
endtime = None
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample,
client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample):
self.series = pandas.Series()
self.metric = metric
self.tags = tags
@@ -84,8 +82,7 @@ class TimelyMetric(TimelyWebSocketClient):
self.data = []
self.timeDateRange = TimeDateRange(beginStr, endStr, period)
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)
TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs())
def debugOn(self):
self.debug = True
@@ -15,14 +15,10 @@ class WebSocketClient():
APPLICATION_JSON = 'application/json'
def __init__(self, hostport, path, params, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
request_timeout=DEFAULT_REQUEST_TIMEOUT, client_cert=None, client_key=None, ca_certs=None, validate_cert=False):
request_timeout=DEFAULT_REQUEST_TIMEOUT):
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('/')):
@@ -43,8 +39,7 @@ 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=self.validate_cert,
client_cert=self.client_cert, client_key=self.client_key, ca_certs=self.ca_certs)
request = websocket.httpclient.HTTPRequest(self.url, connect_timeout=self.connect_timeout, request_timeout=self.request_timeout, validate_cert=False)
websocket.websocket_connect(request, io_loop=ioloop, callback=self._connect_callback)
def send(self, data):
@@ -5,9 +5,7 @@ import os
from timely.TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
hostport = "127.0.0.1:5244"
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()
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None).fetch()
print(timelyMetric.getDataFrame())
exit()
+10 -26
View File
@@ -15,27 +15,15 @@
<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>
<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>
@@ -57,9 +45,5 @@
<!-- 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 == null ? null : token.getTimelyPrincipal().getName());
buf.append("Token: ").append(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 final Logger LOG = LoggerFactory.getLogger(AuthCache.class);
private static 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();
}
private static Cache<String, TimelyPrincipal> getCache() {
public static Cache<String, TimelyPrincipal> getCache() {
if (-1 == sessionMaxAge) {
throw new IllegalStateException("Cache session max age not configured.");
}
@@ -49,32 +49,10 @@ 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 = AuthCache.get(entityName);
TimelyPrincipal principal = CACHE.asMap().get(entityName);
if (principal != null) {
for (Collection<String> authCollection : principal.getAuthorizations()) {
authorizationsList.add(AuthorizationsUtil.toAuthorizations(authCollection));
@@ -2,14 +2,8 @@ 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;
@@ -26,28 +20,21 @@ 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.DnUtils;
import timely.auth.util.AuthenticationUtils;
import timely.netty.http.auth.TimelyAuthenticationToken;
import timely.netty.http.auth.TimelyUserDetails;
public class AuthenticationService {
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationService.class);
private static 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 = springContext.getBean("authenticationManager", AuthenticationManager.class);
x509 = springContext.getBean("x509PrincipalExtractor", SubjectDnX509PrincipalExtractor.class);
requiredRoles = springContext.getBean("requiredRoles", List.class);
requiredAuths = springContext.getBean("requiredAuths", List.class);
authManager = (AuthenticationManager) springContext.getBean("authenticationManager");
x509 = (SubjectDnX509PrincipalExtractor) springContext.getBean("x509PrincipalExtractor");
} catch (BeansException e) {
throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(),
e.getRootCause());
@@ -58,54 +45,23 @@ public class AuthenticationService {
return authManager;
}
public static TimelyPrincipal authenticate(Authentication authentication, String cacheName) {
return authenticate(authentication, SubjectIssuerDNPair.of(cacheName), cacheName);
public static TimelyAuthenticationToken authenticate(Authentication authentication) {
return authenticate(authentication, null);
}
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 TimelyAuthenticationToken authenticate(Authentication authentication, X509Certificate clientCert) {
Authentication token = getAuthenticationManager().authenticate(authentication);
return AuthenticationUtils.getTimelyAuthenticationToken(token, clientCert);
}
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)) {
verifiedPrincipal = AuthCache.get(sessionId);
if (verifiedPrincipal == null) {
if (!AuthCache.getCache().asMap().containsKey(sessionId)) {
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
"Unknown session id was submitted, log in again");
}
@@ -113,62 +69,26 @@ public class AuthenticationService {
try {
TimelyAuthenticationToken token = request.getToken();
TimelyPrincipal requestPrincipal = token.getTimelyPrincipal();
String entity = requestPrincipal.getName();
if (!AuthCache.containsKey(requestPrincipal.getName())) {
Object newObj = new Object();
Object o = fetchingPrincipal.getOrDefault(entity, newObj);
if (o == null) {
o = newObj;
}
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);
}
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);
}
} finally {
fetchingPrincipal.remove(entity);
authenticatedTimelyUsers.addAll(entityPrincipal.getProxiedUsers());
}
} else {
LOG.trace("Verified principal {} in AuthCache on 1st attempt", entity);
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());
}
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());
@@ -215,21 +135,4 @@ 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(primaryUser);
this.proxiedUsers.add(first);
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.DnUtils;
import timely.auth.util.ProxiedEntityUtils;
/**
* A user of a TIMELY service. Typically, one or more of these users (a chain
@@ -38,28 +38,11 @@ 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(), -1L);
this(SubjectIssuerDNPair.of(subjectDn), UserType.USER, auths, null, null, System.currentTimeMillis(), -1);
}
public TimelyUser(SubjectIssuerDNPair dn, UserType userType, Collection<String> auths, Collection<String> roles,
@@ -75,7 +58,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 = DnUtils.getCommonName(dn.subjectDN());
this.commonName = ProxiedEntityUtils.getCommonName(dn.subjectDN());
this.dn = dn;
this.userType = userType;
this.auths = auths == null ? Collections.emptyList() : new LinkedHashSet<>(auths);
@@ -3,6 +3,7 @@ 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;
@@ -10,20 +11,18 @@ 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, TimelyUser> users;
private HashMap<String, List<String>> users;
public Map<String, TimelyUser> getUsers() {
public Map<String, List<String>> getUsers() {
return users;
}
public void setUsers(List<TimelyUser> users) {
this.users = new HashMap<>();
users.forEach(u -> this.users.put(u.getName(), u));
public void setUsers(LinkedHashMap<String, List<String>> users) {
this.users = new HashMap<>(users);
}
@Override
@@ -31,32 +30,25 @@ public class UserDetailsService implements
throws UsernameNotFoundException {
// Determine if the user is allowed to access the system, if not throw
// UsernameNotFoundException
String username = token.getName();
String username = token.getName().toString();
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 TimelyUserDetails() {
return new UserDetails() {
private static final long serialVersionUID = 1L;
@Override
public Collection<SimpleGrantedAuthority> getAuthorities() {
final Collection<SimpleGrantedAuthority> auths = new ArrayList<>();
users.get(username).getAuths().forEach(a -> {
users.get(username).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 "";
@@ -0,0 +1,48 @@
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 proxiedEntities = HttpHeaderUtils.getSingleHeader(headers,
String proxiesEntities = HttpHeaderUtils.getSingleHeader(headers,
TimelyAuthenticationToken.PROXIED_ENTITIES_HEADER, false);
headers.removeAll(TimelyAuthenticationToken.PROXIED_ENTITIES_HEADER);
String[] preExistingProxiedEntities = ProxiedEntityUtils.splitProxiedDNs(proxiedEntities, false);
String[] preExistingProxiedEntities = ProxiedEntityUtils.splitProxiedDNs(proxiesEntities, 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 proxiedIssuers = HttpHeaderUtils.getSingleHeader(headers,
String proxiesIssuers = HttpHeaderUtils.getSingleHeader(headers,
TimelyAuthenticationToken.PROXIED_ISSUERS_HEADER, false);
headers.removeAll(TimelyAuthenticationToken.PROXIED_ISSUERS_HEADER);
String[] preExistingProxiedIssuers = ProxiedEntityUtils.splitProxiedDNs(proxiedIssuers, false);
String[] preExistingProxiedIssuers = ProxiedEntityUtils.splitProxiedDNs(proxiesIssuers, 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.containsKey(sessionId)) {
principal = AuthCache.get(sessionId);
if (AuthCache.getCache().asMap().containsKey(sessionId)) {
principal = AuthCache.getCache().asMap().get(sessionId);
} else {
principal = TimelyPrincipal.anonymousPrincipal();
}
@@ -20,12 +20,13 @@ public class BasicAuthLoginRequestHandler extends TimelyLoginRequestHandler<Basi
}
@Override
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, BasicAuthLoginRequest msg, String sessionId)
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, BasicAuthLoginRequest msg)
throws AuthenticationException {
// Perform the login process using username/password
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(msg.getUsername(),
msg.getPassword());
TimelyPrincipal principal = AuthenticationService.authenticate(token, sessionId);
TimelyAuthenticationToken timelyToken = AuthenticationService.authenticate(token);
TimelyPrincipal principal = timelyToken.getTimelyPrincipal();
LOG.debug("Authenticated user {} with authorizations {}", principal.getPrimaryUser().getDn().subjectDN(),
principal.getAuthorizationsString());
@@ -22,19 +22,21 @@ 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.getIssuerDN().getName();
String issuerDn = clientCert == null ? null : clientCert.getSubjectDN().getName();
String proxiedEntities;
String proxiedIssuers;
try {
@@ -49,7 +51,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,6 +15,7 @@ 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;
@@ -37,9 +38,10 @@ 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();
TimelyPrincipal principal = authenticate(ctx, loginRequest, sessionId);
LOG.trace("Authenticated new sessionId {} for user {}", sessionId,
AuthCache.getCache().put(sessionId, principal);
LOG.debug("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);
@@ -64,6 +66,5 @@ public abstract class TimelyLoginRequestHandler<T> extends SimpleChannelInboundH
}
protected abstract TimelyPrincipal authenticate(ChannelHandlerContext ctx, T loginRequest, String entity)
throws Exception;
protected abstract TimelyPrincipal authenticate(ChannelHandlerContext ctx, T loginRequest) throws Exception;
}
@@ -1,10 +0,0 @@
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,36 +9,34 @@ 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 final Logger LOG = LoggerFactory.getLogger(X509LoginRequestHandler.class);
private static Logger LOG = LoggerFactory.getLogger(X509LoginRequestHandler.class);
public X509LoginRequestHandler(Security security, Http http) {
super(security, http);
}
@Override
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, X509LoginRequest loginRequest, String sessionId)
throws Exception {
protected TimelyPrincipal authenticate(ChannelHandlerContext ctx, X509LoginRequest loginRequest) 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());
SubjectIssuerDNPair pair = SubjectIssuerDNPair.of(clientCert.getSubjectDN().getName(),
clientCert.getIssuerDN().getName());
principal = AuthenticationService.authenticate(token, pair, sessionId);
TimelyAuthenticationToken t = AuthenticationService.authenticate(token, clientCert);
principal = t.getTimelyPrincipal();
LOG.debug("Authenticated user {} with authorizations {}",
principal.getPrimaryUser().getDn().subjectDN(), principal.getAuthorizationsString());
}
@@ -50,9 +50,11 @@ 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);
@@ -61,8 +63,8 @@ public class WebSocketRequestDecoder extends MessageToMessageDecoder<WebSocketFr
if (StringUtils.isNotBlank(sessionId)) {
TimelyPrincipal principal;
((AuthenticatedRequest) request).setSessionId(sessionId);
if (AuthCache.containsKey(sessionId)) {
principal = AuthCache.get(sessionId);
if (AuthCache.getCache().asMap().containsKey(sessionId)) {
principal = AuthCache.getCache().asMap().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 [{}] with auths: {}", request.getUserName(), auths);
LOG.debug("Creating metric scanner for session: {} with auths: {}", request.getSessionId(), 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) {
visibilityFilters.add(new VisibilityFilter(a));
((ArrayList<VisibilityFilter>) 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(this.request, metric, tags, oldestTsForMetric,
metricsFromCache = cache.getMetricsFromCache(request, metric, tags, oldestTsForMetric,
endTimeStamp);
LOG.debug("[{}] MetricScanner request partially fulfilled from cache: {} metrics",
subscriptionId, metricsFromCache.size());
} else {
metricsFromCache = cache.getMetricsFromCache(this.request, metric, tags, this.beginTime,
metricsFromCache = cache.getMetricsFromCache(request, metric, tags, this.beginTime,
endTimeStamp);
LOG.debug("[{}] Websocket request completely fulfilled from cache: {} metrics [{}]",
subscriptionId, metricsFromCache.size());
+1 -1
View File
@@ -1,2 +1,2 @@
npe.ou.entries=iamnotaperson,npe,stillnotaperson
subject.dn.pattern=(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)
subject.dn.pattern=(?:^|,)\\s*OU\\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,16 +28,12 @@ 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");
AuthenticationService.authenticate(token, cookie);
TimelyAuthenticationToken auth = AuthenticationService.authenticate(token);
AuthCache.getCache().put(cookie, auth.getTimelyPrincipal());
}
@After
@@ -2,54 +2,31 @@ 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, "test");
AuthenticationService.authenticate(token);
}
@Test
public void testBasicAuthenticationLogin() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
TimelyPrincipal principal = AuthenticationService.authenticate(token, "test");
Collection<? extends Collection<String>> authorizations = principal.getAuthorizations();
Authentication auth = AuthenticationService.authenticate(token);
Collection<? extends GrantedAuthority> authorizations = auth.getAuthorities();
authorizations.forEach(a -> {
Assert.assertTrue(a.contains("A") || a.contains("B") || a.contains("C"));
Assert.assertTrue(
a.getAuthority().equals("A") || a.getAuthority().equals("B") || a.getAuthority().equals("C"));
});
}
@@ -57,10 +34,11 @@ public class AuthenticationServiceTest {
public void testX509AuthenticationLogin() {
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken("CN=example.com",
"doesn't matter what I put here");
TimelyPrincipal principal = AuthenticationService.authenticate(token, "test");
Collection<? extends Collection<String>> authorizations = principal.getAuthorizations();
Authentication auth = AuthenticationService.authenticate(token);
Collection<? extends GrantedAuthority> authorizations = auth.getAuthorities();
authorizations.forEach(a -> {
Assert.assertTrue(a.contains("D") || a.contains("E") || a.contains("F"));
Assert.assertTrue(
a.getAuthority().equals("D") || a.getAuthority().equals("E") || a.getAuthority().equals("F"));
});
}
@@ -68,10 +46,11 @@ public class AuthenticationServiceTest {
public void testX509AuthenticationLoginFailed() {
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken("CN=bad.example.com",
"doesn't matter what I put here");
TimelyPrincipal principal = AuthenticationService.authenticate(token, "test");
Collection<? extends Collection<String>> authorizations = principal.getAuthorizations();
Authentication auth = AuthenticationService.authenticate(token);
Collection<? extends GrantedAuthority> authorizations = auth.getAuthorities();
authorizations.forEach(a -> {
Assert.assertTrue(a.contains("D") || a.contains("E") || a.contains("F"));
Assert.assertTrue(
a.getAuthority().equals("D") || a.getAuthority().equals("E") || a.getAuthority().equals("F"));
});
}
@@ -1,45 +0,0 @@
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,6 +38,7 @@ 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;
@@ -72,7 +73,8 @@ public class HttpRequestDecoderTest {
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
AuthCache.setSessionMaxAge(config.getSecurity());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
AuthenticationService.authenticate(token, cookie);
TimelyAuthenticationToken auth = AuthenticationService.authenticate(token);
AuthCache.getCache().put(cookie, auth.getTimelyPrincipal());
}
@AfterClass
@@ -57,7 +57,6 @@ public class BasicAuthLoginRequestHandlerTest {
@Before
public void setup() throws Exception {
AuthCache.clear();
results.clear();
}
@@ -30,6 +30,7 @@ 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;
@@ -66,7 +67,8 @@ public class WebSocketRequestDecoderTest {
cookie = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
AuthCache.setSessionMaxAge(config.getSecurity());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test", "test1");
AuthCache.put(cookie, new TimelyPrincipal(TimelyUser.ANONYMOUS_USER));
AuthCache.getCache().put(cookie,
new TimelyPrincipal(new TimelyUser("ANONYMOUS", AuthenticationUtils.getAuthCollection(token))));
}
@AfterClass
@@ -12,7 +12,6 @@ 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;
@@ -84,8 +83,4 @@ public class MacITBase {
conf.setMetricAgeOffDays(ageOffSettings);
}
@Before
public void initializeAuthCache() {
AuthCache.clear();
}
}
@@ -80,7 +80,6 @@ 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,6 +71,7 @@ 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;
@@ -195,7 +196,7 @@ public class WebSocketIT extends OneWaySSLBase {
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
this.sessionId = UUID.randomUUID().toString();
AuthCache.put(sessionId, TimelyPrincipal.anonymousPrincipal());
AuthCache.getCache().put(sessionId, TimelyPrincipal.anonymousPrincipal());
group = new NioEventLoopGroup();
SslContext ssl = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
@@ -504,8 +505,9 @@ public class WebSocketIT extends OneWaySSLBase {
@Test
public void testSubscriptionWorkflowWithEndTimeAndVisibilities() throws Exception {
AuthCache.remove(sessionId);
AuthenticationService.authenticate(token, sessionId);
TimelyAuthenticationToken auth = AuthenticationService.authenticate(token);
TimelyPrincipal principal = auth.getTimelyPrincipal();
AuthCache.getCache().put(sessionId, principal);
try {
final String subscriptionId = "1234";
@@ -583,7 +585,7 @@ public class WebSocketIT extends OneWaySSLBase {
close.setSubscriptionId(subscriptionId);
ch.writeAndFlush(new TextWebSocketFrame(JsonUtil.getObjectMapper().writeValueAsString(close)));
} finally {
AuthCache.remove(sessionId);
AuthCache.getCache().asMap().remove(sessionId, principal);
ch.close().sync();
s.shutdown();
group.shutdownGracefully();
+1 -1
View File
@@ -1,2 +1,2 @@
npe.ou.entries=iamnotaperson,npe,stillnotaperson
subject.dn.pattern=(?:^|,)\\s*OU\\s*=\\s*My Department\\s*(?:,|$)
subject.dn.pattern=(?:^|,)\\s*OU\\s*My Department\\s*(?:,|$)
+14 -26
View File
@@ -11,33 +11,21 @@
<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: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="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="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />