Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement UserMap and GroupMap output. #159

Merged
merged 7 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cadc-access-control-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sourceCompatibility = 1.8

group = 'org.opencadc'

version = '1.3.33'
version = '1.3.34'

description = 'OpenCADC User+Group server library'
def git_url = 'https://github.com/opencadc/ac'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
import ca.nrc.cadc.auth.NumericPrincipal;
import ca.nrc.cadc.auth.SignedToken;
import ca.nrc.cadc.net.TransientException;
import ca.nrc.cadc.util.FileUtil;
import ca.nrc.cadc.util.MultiValuedProperties;
import ca.nrc.cadc.util.PropertiesReader;
import ca.nrc.cadc.util.RsaSignatureGenerator;
Expand Down Expand Up @@ -335,6 +334,7 @@ private static Map<String, Object> buildTokenClaimsSet(RelyParty rp, String requ

if (rp.getClaims().contains(RelyParty.Claim.NAME)) {
claimsMap.put(RelyParty.Claim.NAME.getValue(), useridPrincipal.getName());
claimsMap.put(RelyParty.Claim.PREFERRED_USERNAME.getValue(), useridPrincipal.getName());
}
if (rp.getClaims().contains(RelyParty.Claim.EMAIL)) {
claimsMap.put(RelyParty.Claim.EMAIL.getValue(), email);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public boolean equals(Object o) {

public enum Claim {
NAME("name", "Name"),
PREFERRED_USERNAME("preferred_username", "Username"),
EMAIL("email", "Email Address"),
GROUPS("memberOf", "Group Memberships");

Expand Down
2 changes: 1 addition & 1 deletion cadc-gms/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ sourceCompatibility = 1.8

group = 'org.opencadc'

version = '1.0.8'
version = '1.0.9'

description = 'OpenCADC GMS API library'
def git_url = 'https://github.com/opencadc/ac'
Expand Down
161 changes: 119 additions & 42 deletions cadc-gms/src/main/java/org/opencadc/auth/PosixMapperClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
import ca.nrc.cadc.auth.AuthenticationUtil;
import ca.nrc.cadc.auth.HttpPrincipal;
import ca.nrc.cadc.auth.PosixPrincipal;
import ca.nrc.cadc.cred.client.CredUtil;
import ca.nrc.cadc.net.HttpGet;
import ca.nrc.cadc.net.ResourceAlreadyExistsException;
import ca.nrc.cadc.net.ResourceNotFoundException;
Expand All @@ -86,10 +85,7 @@
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.security.AccessControlException;
import java.security.Principal;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
Expand All @@ -109,14 +105,16 @@ public class PosixMapperClient {

private final String service;
private final Capabilities capabilities;
private final RegistryClient regClient = new RegistryClient();
private final TSVPosixGroupParser posixGroupMarshaller = new TSVPosixGroupParser();
private final TSVPosixPrincipalParser posixPrincipalMarshaller = new TSVPosixPrincipalParser();

public PosixMapperClient(URI resourceID) {
if (resourceID == null) {
throw new IllegalArgumentException("resourceID cannot be null");
}
this.service = resourceID.toASCIIString();
try {
final RegistryClient regClient = new RegistryClient();
this.capabilities = regClient.getCapabilities(resourceID);
} catch (ResourceNotFoundException | IOException ex) {
throw new RuntimeException("failed to read capabilities for " + service, ex);
Expand Down Expand Up @@ -186,23 +184,11 @@ public Subject augment(Subject subject)

try (BufferedReader reader = new BufferedReader(new InputStreamReader(get.getInputStream()))) {
String line = reader.readLine();
String[] tokens = line.split("\\s");
if (tokens.length != 3) {
throw new IllegalStateException(
String.format("error parsing query results, expected 3 values, found %s: %s",
tokens.length, line));
}
// format - username uid gid
String username = tokens[0];
int userID = Integer.parseInt(tokens[1]);
int groupID = Integer.parseInt(tokens[2]);
final PosixPrincipal posixPrincipal = posixPrincipalMarshaller.parse(line);

Set<Principal> principals = new HashSet<>();
PosixPrincipal posixPrincipal = new PosixPrincipal(userID);
posixPrincipal.username = username;
posixPrincipal.defaultGroup = groupID;
principals.add(posixPrincipal);
principals.add(new HttpPrincipal(username));
principals.add(new HttpPrincipal(posixPrincipal.username));
for (Principal p : subject.getPrincipals()) {
if (!(p instanceof HttpPrincipal) && !(p instanceof PosixPrincipal)) {
principals.add(p);
Expand Down Expand Up @@ -233,19 +219,88 @@ public List<PosixGroup> getURI(List<Integer> groups)
return getPosixGroups(null, groups);
}

// use case: skaha needs the complete username-uid map for user containers
// change: adding defaultGroup and username to PosixPrincipal, all fields would be returned here
// note: Iterator allows the client to consume the stream and process it without having to
// store it in memory... scalable but sometimes awkward
public Iterator<PosixPrincipal> getUserMap() {
throw new UnsupportedOperationException();
/**
* Obtain the full mapping of Username to UID mapping to populate the POSIX system mappings. It is assumed
* the underlying API supports writing TSV output.
* <p></p>
* use case: skaha needs the complete username-uid map for user containers
* change: adding defaultGroup and username to PosixPrincipal, all fields would be returned here
* note: Iterator allows the client to consume the stream and process it without having to
* store it in memory... scalable but sometimes awkward
*
* @return Iterator over POSIX principals
* @throws IOException Any I/O related (catch-all) errors.
* @throws ResourceNotFoundException If the UID POSIX Mapping service cannot be found.
* @throws ResourceAlreadyExistsException Not used.
* @throws InterruptedException Should not ever happen.
*/
public Iterator<PosixPrincipal> getUserMap() throws IOException, ResourceNotFoundException,
ResourceAlreadyExistsException, InterruptedException {
final URL userMapURL = getServiceURL(Standards.POSIX_USERMAP);
final HttpGet get = new HttpGet(userMapURL, true);
get.setRequestProperty("accept", "text/tab-separated-values");
get.prepare();

return new Iterator<PosixPrincipal>() {
private final BufferedReader reader = new BufferedReader(new InputStreamReader(get.getInputStream()));
private String line;

@Override
public boolean hasNext() {
try {
return reader.ready() && (line = reader.readLine()) != null;
} catch (IOException ioException) {
throw new RuntimeException(ioException.getMessage(), ioException);
}
}

@Override
public PosixPrincipal next() {
final PosixPrincipal nextPrincipal = posixPrincipalMarshaller.parse(line);
line = null;
return nextPrincipal;
}
};
}

// use case: skaha needs the complete groupname-gid map for user containers
// note: Iterator allows the client to consume the stream and process it without having to
// store it in memory... scalable but sometimes awkward
public Iterator<PosixGroup> getGroupMap() {
throw new UnsupportedOperationException();
/**
* Obtain the full Group Name to GID Mapping. It is assumed the underlying API supports writing TSV output.
* <p></p>
* use case: skaha needs the complete groupname-gid map for user containers
* note: Iterator allows the client to consume the stream and process it without having to
* store it in memory... scalable but sometimes awkward
* @return @return Iterator over POSIX Groups
* @throws IOException Any I/O related (catch-all) errors.
* @throws ResourceNotFoundException If the GID POSIX Mapping service cannot be found.
* @throws ResourceAlreadyExistsException Not used.
* @throws InterruptedException Should not ever happen.
*/
public Iterator<PosixGroup> getGroupMap() throws IOException, ResourceNotFoundException,
ResourceAlreadyExistsException, InterruptedException {
final URL userMapURL = getServiceURL(Standards.POSIX_GROUPMAP);
final HttpGet get = new HttpGet(userMapURL, true);
get.setRequestProperty("accept", "text/tab-separated-values");
get.prepare();

return new Iterator<PosixGroup>() {
private final BufferedReader reader = new BufferedReader(new InputStreamReader(get.getInputStream()));
private String line;
@Override
public boolean hasNext() {
try {
return reader.ready() && (line = reader.readLine()) != null;
} catch (IOException ioException) {
throw new RuntimeException(ioException.getMessage(), ioException);
}
}

@Override
public PosixGroup next() {
final PosixGroup posixGroup = posixGroupMarshaller.parse(line);
line = null;
return posixGroup;
}
};
}

private List<PosixGroup> getPosixGroups(List<GroupURI> groupURIs, List<Integer> groupGIDs)
Expand Down Expand Up @@ -276,23 +331,13 @@ private List<PosixGroup> getPosixGroups(List<GroupURI> groupURIs, List<Integer>
List<PosixGroup> posixGroups = new ArrayList<>();
while (reader.ready()) {
String line = reader.readLine();
log.debug("line: " + line);
String[] tokens = line.split("\\s+");
if (tokens.length != 2) {
throw new IllegalStateException(
String.format("error parsing query results, expected 2 values, found %s: %s",
tokens.length, line));
}
GroupURI groupURI = new GroupURI(URI.create(tokens[0]));
Integer gid = Integer.parseInt(tokens[1]);
posixGroups.add(new PosixGroup(gid, groupURI));
posixGroups.add(posixGroupMarshaller.parse(line));
}
return posixGroups;
}
}

private URL getServiceURL(URI standardID)
throws IOException, ResourceNotFoundException {
private URL getServiceURL(URI standardID) {
// this probably failed in ctor already
if (capabilities == null) {
throw new RuntimeException("BUG: capabilities not found and went undetected");
Expand All @@ -315,4 +360,36 @@ private URL getServiceURL(URI standardID)
return iface.getAccessURL().getURL();
}

private static class TSVPosixGroupParser {
final PosixGroup parse(final String line) {
log.debug("line: " + line);
final String[] tokens = line.split("\\s+");
if (tokens.length != 2) {
throw new IllegalStateException(
String.format("error parsing query results, expected 2 values, found %s: %s",
tokens.length, line));
}

final GroupURI groupURI = new GroupURI(URI.create(tokens[0]));
final Integer gid = Integer.parseInt(tokens[1]);

return new PosixGroup(gid, groupURI);
}
}

private static class TSVPosixPrincipalParser {
final PosixPrincipal parse(final String line) {
log.debug("line: " + line);
final String[] tokens = line.split("\\s+");
if (tokens.length != 3) {
throw new IllegalStateException(
String.format("error parsing query results, expected 3 values, found %s: %s",
tokens.length, line));
}
final PosixPrincipal posixPrincipal = new PosixPrincipal(Integer.parseInt(tokens[1]));
posixPrincipal.username = tokens[0];
posixPrincipal.defaultGroup = Integer.parseInt(tokens[2]);
return posixPrincipal;
}
}
}
Loading