-
Notifications
You must be signed in to change notification settings - Fork 592
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Kerberos auth to local rules support
- Loading branch information
Showing
20 changed files
with
1,035 additions
and
189 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
...ain/java/com/linkedin/kafka/cruisecontrol/servlet/security/DummyAuthorizationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Copyright 2023 LinkedIn Corp. Licensed under the BSD 2-Clause License (the "License"). See License in the project root for license information. | ||
*/ | ||
|
||
package com.linkedin.kafka.cruisecontrol.servlet.security; | ||
|
||
import org.eclipse.jetty.security.SpnegoUserIdentity; | ||
import org.eclipse.jetty.security.SpnegoUserPrincipal; | ||
import org.eclipse.jetty.security.authentication.AuthorizationService; | ||
import org.eclipse.jetty.server.UserIdentity; | ||
import org.eclipse.jetty.util.security.Credential; | ||
import javax.security.auth.Subject; | ||
import javax.servlet.http.HttpServletRequest; | ||
import java.security.Principal; | ||
|
||
public class DummyAuthorizationService implements AuthorizationService { | ||
|
||
private static final Credential NO_CREDENTIAL = new Credential() { | ||
@Override | ||
public boolean check(Object credentials) { | ||
return false; | ||
} | ||
}; | ||
|
||
@Override | ||
public UserIdentity getUserIdentity(HttpServletRequest request, String name) { | ||
return createUserIdentity(name); | ||
} | ||
|
||
private UserIdentity createUserIdentity(String username) { | ||
Principal userPrincipal = new SpnegoUserPrincipal(username, ""); | ||
Subject subject = new Subject(); | ||
subject.getPrincipals().add(userPrincipal); | ||
subject.getPrivateCredentials().add(NO_CREDENTIAL); | ||
subject.setReadOnly(); | ||
|
||
return new SpnegoUserIdentity(subject, userPrincipal, null); | ||
} | ||
|
||
} |
64 changes: 64 additions & 0 deletions
64
...src/main/java/com/linkedin/kafka/cruisecontrol/servlet/security/spnego/PrincipalName.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* Copyright 2023 LinkedIn Corp. Licensed under the BSD 2-Clause License (the "License"). See License in the project root for license information. | ||
*/ | ||
|
||
package com.linkedin.kafka.cruisecontrol.servlet.security.spnego; | ||
|
||
import java.util.Objects; | ||
|
||
public class PrincipalName { | ||
private final String _primary; | ||
private final String _instance; | ||
private final String _realm; | ||
|
||
public PrincipalName(String primary, String instance, String realm) { | ||
_primary = Objects.requireNonNull(primary, "primary must not be null"); | ||
_instance = instance; | ||
_realm = realm; | ||
} | ||
|
||
public PrincipalName(String primary) { | ||
_primary = Objects.requireNonNull(primary, "primary must not be null"); | ||
_instance = null; | ||
_realm = null; | ||
} | ||
|
||
public String getPrimary() { | ||
return _primary; | ||
} | ||
|
||
public String getInstance() { | ||
return _instance; | ||
} | ||
|
||
public String getRealm() { | ||
return _realm; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || !Objects.equals(getClass(), o.getClass())) { | ||
return false; | ||
} | ||
PrincipalName principalName = (PrincipalName) o; | ||
return _primary.equals(principalName._primary) && Objects.equals(_instance, principalName._instance) | ||
&& Objects.equals(_realm, principalName._realm); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(_primary, _instance, _realm); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "PrincipalName{" | ||
+ "primary='" + _primary + '\'' | ||
+ ", instance='" + _instance + '\'' | ||
+ ", realm='" + _realm + '\'' | ||
+ '}'; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...ain/java/com/linkedin/kafka/cruisecontrol/servlet/security/spnego/PrincipalValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2023 LinkedIn Corp. Licensed under the BSD 2-Clause License (the "License"). See License in the project root for license information. | ||
*/ | ||
|
||
package com.linkedin.kafka.cruisecontrol.servlet.security.spnego; | ||
|
||
import org.apache.kafka.common.config.ConfigDef.Validator; | ||
import org.apache.kafka.common.config.ConfigException; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class PrincipalValidator implements Validator { | ||
private static final Pattern PRINCIPAL_REGEX = | ||
Pattern.compile("(?<primary>[^/\\s@]+)(/(?<instance>[\\w.-]+))?(@(?<realm>(\\S+)))?"); | ||
|
||
private final boolean _instanceRequired; | ||
private final boolean _realmRequired; | ||
|
||
public PrincipalValidator(boolean instanceRequired, boolean realmRequired) { | ||
_instanceRequired = instanceRequired; | ||
_realmRequired = realmRequired; | ||
} | ||
|
||
/** | ||
* Creates a PrincipalName object. | ||
* @param configName The name of the configuration | ||
* @param principal The principal which will be the base of the PrincipalName object | ||
* @return PrincipalName object | ||
*/ | ||
public static PrincipalName parsePrincipal(String configName, String principal) { | ||
Matcher matcher = PRINCIPAL_REGEX.matcher(principal); | ||
if (!matcher.matches()) { | ||
throw new ConfigException(configName, principal, "Invalid principal"); | ||
} | ||
String primary = matcher.group("primary"); | ||
String instance = matcher.group("instance"); | ||
String realm = matcher.group("realm"); | ||
return new PrincipalName(primary, instance, realm); | ||
} | ||
|
||
@Override | ||
public void ensureValid(String name, Object value) { | ||
if (value == null) { | ||
return; | ||
} | ||
|
||
if (!(value instanceof String)) { | ||
throw new ConfigException(name, value, "Value must be string"); | ||
} | ||
|
||
String strVal = (String) value; | ||
PrincipalName principalName = parsePrincipal(name, strVal); | ||
if (_instanceRequired && principalName.getInstance() == null) { | ||
throw new ConfigException(name, strVal, "Principal must contain the instance section"); | ||
} | ||
if (_realmRequired && principalName.getRealm() == null) { | ||
throw new ConfigException(name, strVal, "Principal must contain the realm section"); | ||
} | ||
} | ||
} |
Oops, something went wrong.