/* * Copyright 2016 Analytical Graphics, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.keycloak.authentication.authenticators.x509; import freemarker.template.utility.NullArgumentException; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.keycloak.services.ServicesLogger; import java.security.cert.X509Certificate; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author <a href="mailto:pnalyvayko@agi.com">Peter Nalyvayko</a> * @version $Revision: 1 $ * @date 7/30/2016 */ public abstract class UserIdentityExtractor { private static final ServicesLogger logger = ServicesLogger.LOGGER; public abstract Object extractUserIdentity(X509Certificate[] certs); static class OrExtractor extends UserIdentityExtractor { UserIdentityExtractor extractor; UserIdentityExtractor other; OrExtractor(UserIdentityExtractor extractor, UserIdentityExtractor other) { this.extractor = extractor; this.other = other; if (this.extractor == null) throw new NullArgumentException("extractor"); if (this.other == null) throw new NullArgumentException("other"); } @Override public Object extractUserIdentity(X509Certificate[] certs) { Object result = this.extractor.extractUserIdentity(certs); if (result == null) result = this.other.extractUserIdentity(certs); return result; } } static class X500NameRDNExtractor extends UserIdentityExtractor { private ASN1ObjectIdentifier x500NameStyle; Function<X509Certificate[],X500Name> x500Name; X500NameRDNExtractor(ASN1ObjectIdentifier x500NameStyle, Function<X509Certificate[],X500Name> x500Name) { this.x500NameStyle = x500NameStyle; this.x500Name = x500Name; } @Override public Object extractUserIdentity(X509Certificate[] certs) { if (certs == null || certs.length == 0) throw new IllegalArgumentException(); X500Name name = x500Name.apply(certs); if (name != null) { RDN[] rnds = name.getRDNs(x500NameStyle); if (rnds != null && rnds.length > 0) { RDN cn = rnds[0]; return IETFUtils.valueToString(cn.getFirst().getValue()); } } return null; } } static class PatternMatcher extends UserIdentityExtractor { private final String _pattern; private final Function<X509Certificate[],String> _f; PatternMatcher(String pattern, Function<X509Certificate[],String> valueToMatch) { _pattern = pattern; _f = valueToMatch; } @Override public Object extractUserIdentity(X509Certificate[] certs) { String value = _f.apply(certs); Pattern r = Pattern.compile(_pattern, Pattern.CASE_INSENSITIVE); Matcher m = r.matcher(value); if (!m.find()) { logger.debugf("[PatternMatcher:extract] No matches were found for input \"%s\", pattern=\"%s\"", value, _pattern); return null; } if (m.groupCount() != 1) { logger.debugf("[PatternMatcher:extract] Match produced more than a single group for input \"%s\", pattern=\"%s\"", value, _pattern); return null; } return m.group(1); } } static class OrBuilder { UserIdentityExtractor extractor; UserIdentityExtractor other; OrBuilder(UserIdentityExtractor extractor) { this.extractor = extractor; } public UserIdentityExtractor or(UserIdentityExtractor other) { return new OrExtractor(extractor, other); } } public static UserIdentityExtractor getPatternIdentityExtractor(String pattern, Function<X509Certificate[],String> func) { return new PatternMatcher(pattern, func); } public static UserIdentityExtractor getX500NameExtractor(ASN1ObjectIdentifier identifier, Function<X509Certificate[],X500Name> x500Name) { return new X500NameRDNExtractor(identifier, x500Name); } public static OrBuilder either(UserIdentityExtractor extractor) { return new OrBuilder(extractor); } }