From ed9a5783764ff990648e59f3b0f6defe48294e7d Mon Sep 17 00:00:00 2001 From: Tigran Mkrtchyan Date: Wed, 21 Oct 2020 17:35:15 +0200 Subject: [PATCH] pol: update to oncrpc4j-3.1.0 Motivation: new major version update with improvements and breaking changes: - Drop dependency on dcache-auth , which had incompatible license - Experimental AUTH_TLS support - Various javadoc improvements - Improved code coverage - removed direct dependency on grizzly-framework-monitoring The result of removal of license incompatible dcache-auth in oncrpc4j is migration from org.dcache.auth.GidPrincipal and org.dcache.auth.UidPrincipal to com.sun.security.auth.UnixNumericGroupPrincipal and com.sun.security.auth.UnixNumericUserPrincipa. This breaking change have to be handled in nfs4j. Modification: Update pom to use oncrpc4j-3.1.0. Introduce UnixSubjects that provides alternative to org.dcache.auth.Subjects. Update code base to match API changes. Result: The nfs4j has no dependency with conflicting license. Support for nfs-over-tls. WARNING: this is a breaking change that requires changes in VirtualFileSystem implementations. Acked-by: Paul Millar Target: master --- API-changes.md | 3 + .../org/dcache/nfs/util/UnixSubjects.java | 157 ++++++++++++++++++ .../java/org/dcache/nfs/v3/NfsServerV3.java | 8 +- .../org/dcache/nfs/v4/CompoundContext.java | 5 +- .../java/org/dcache/nfs/vfs/PseudoFs.java | 26 +-- .../org/dcache/nfs/util/UnixSubjectsTest.java | 108 ++++++++++++ .../org/dcache/nfs/v4/OperationOPENTest.java | 6 +- .../java/org/dcache/nfs/vfs/DummyVFS.java | 12 +- .../java/org/dcache/nfs/vfs/PseudoFsTest.java | 98 +++++------ .../java/org/dcache/nfs/vfs/VfsCacheTest.java | 3 +- pom.xml | 2 +- 11 files changed, 351 insertions(+), 77 deletions(-) create mode 100644 core/src/main/java/org/dcache/nfs/util/UnixSubjects.java create mode 100644 core/src/test/java/org/dcache/nfs/util/UnixSubjectsTest.java diff --git a/API-changes.md b/API-changes.md index f19bcaff8..974486234 100644 --- a/API-changes.md +++ b/API-changes.md @@ -4,6 +4,9 @@ - removed deprecated CompoundContextBuilder#withExportFile - removed interface org.dcache.nfs.v4.NfsLoginService +- drop dependency on org.dcache.auth package. Now only UnixNumericUserPrincipal and UnixNumericGroupPrincipal are used. + - this change is not backward compatible due to license incompatibility of org.dcache.auth package. + - added new class org.dcache.nfs.util.UnixSubjects that provides functionality used form org.dcache.auth package.Subjects ## 0.21 diff --git a/core/src/main/java/org/dcache/nfs/util/UnixSubjects.java b/core/src/main/java/org/dcache/nfs/util/UnixSubjects.java new file mode 100644 index 000000000..002358d1f --- /dev/null +++ b/core/src/main/java/org/dcache/nfs/util/UnixSubjects.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020 Deutsches Elektronen-Synchroton, + * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program (see the file COPYING.LIB for more + * details); if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package org.dcache.nfs.util; + +import com.sun.security.auth.UnixNumericGroupPrincipal; +import com.sun.security.auth.UnixNumericUserPrincipal; + +import javax.security.auth.Subject; +import java.util.Arrays; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * A collection of utility methods to manipulate with Unix based subjects. + */ +public class UnixSubjects { + + private UnixSubjects() {} + + /** + * Returns true if and only if subjects contains UnixNumericUserPrincipal with uid 0 (zero). + * @param subject subject to evaluate. + * @return true if subjects contains UnixNumericUserPrincipal with uid 0. + */ + public static boolean isRootSubject(Subject subject) { + return hasUid(subject, 0); + } + + /** + * Returns true if and only if subjects doesn't contain any UnixNumericUserPrincipal. + * @param subject subject to evaluate. + * @return true if subjects doesn't contain any UnixNumericUserPrincipal. + */ + public static boolean isNobodySubject(Subject subject) { + return subject.getPrincipals().stream() + .noneMatch(UnixNumericUserPrincipal.class::isInstance); + } + /** + * Returns true if and only if the subject has the given uid. + * + * @param subject + * @param uid + * @return true, if the subject has given uid.. + */ + public static boolean hasUid(Subject subject, long uid) { + + return subject.getPrincipals().stream() + .filter(UnixNumericUserPrincipal.class::isInstance) + .map(UnixNumericUserPrincipal.class::cast) + .anyMatch(p -> p.longValue() == uid); + } + + /** + * Returns true if and only if the subject has as primary or secondary the given gid. + * + * @param subject + * @param gid + * @return true, if the subject has given gid. + */ + public static boolean hasGid(Subject subject, long gid) { + return subject.getPrincipals().stream() + .filter(UnixNumericGroupPrincipal.class::isInstance) + .map(UnixNumericGroupPrincipal.class::cast) + .anyMatch(p -> p.longValue() == gid); + } + + /** + * Create subject with given uid and gid. + * @param uid users numeric id. + * @param gid users primary group numeric id. + * @return subject with given uid, gid. + */ + public static Subject toSubject(long uid, long gid) { + return new Subject(false, + Set.of(new UnixNumericUserPrincipal(uid), new UnixNumericGroupPrincipal(gid, true)), + Set.of(), + Set.of()); + } + + /** + * Create subject with given uid, primary gid and secondary gids. + * @param uid users numeric id. + * @param gid users primary group numeric id. + * @param gids array of users secondary group numeric ids. + * @return subject with given uid, gid and gids. + */ + public static Subject toSubject(long uid, long gid, long ... gids) { + Subject subject = toSubject(uid, gid); + subject.getPrincipals() + .addAll( + Arrays.stream(gids) + .mapToObj(l -> new UnixNumericGroupPrincipal(l, false)) + .collect(Collectors.toSet()) + ); + return subject; + } + + /** + * Returns the user ID represented by UnixNumericUserPrincipal. + * @param subject subject to evaluate. + * @return the user id. + */ + + public static long getUid(Subject subject) { + return subject.getPrincipals().stream().filter(UnixNumericUserPrincipal.class::isInstance) + .map(UnixNumericUserPrincipal.class::cast) + .mapToLong(UnixNumericUserPrincipal::longValue) + .findFirst() + .getAsLong(); + } + + /** + * Returns the primary group ID of a subject represented by UnixNumericGroupPrincipal. + * @param subject subject to evaluate. + * @return the primary group ID. + */ + public static long getPrimaryGid(Subject subject) { + return subject.getPrincipals().stream().filter(UnixNumericGroupPrincipal.class::isInstance) + .map(UnixNumericGroupPrincipal.class::cast) + .filter(UnixNumericGroupPrincipal::isPrimaryGroup) + .mapToLong(UnixNumericGroupPrincipal::longValue) + .findFirst() + .getAsLong(); + } + + /** + * Returns the secondary group IDs of a subject represented by UnixNumericGroupPrincipal. + * @param subject subject to evaluate. + * @return an array with secondary group IDs, possibly empty. + */ + public static long[] getSecondaryGids(Subject subject) { + return subject.getPrincipals().stream().filter(UnixNumericGroupPrincipal.class::isInstance) + .map(UnixNumericGroupPrincipal.class::cast) + .filter(Predicate.not(UnixNumericGroupPrincipal::isPrimaryGroup)) + .mapToLong(UnixNumericGroupPrincipal::longValue) + .toArray(); + } + +} diff --git a/core/src/main/java/org/dcache/nfs/v3/NfsServerV3.java b/core/src/main/java/org/dcache/nfs/v3/NfsServerV3.java index e5a1b51d4..7f4cd4371 100644 --- a/core/src/main/java/org/dcache/nfs/v3/NfsServerV3.java +++ b/core/src/main/java/org/dcache/nfs/v3/NfsServerV3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 - 2018 Deutsches Elektronen-Synchroton, + * Copyright (c) 2009 - 2020 Deutsches Elektronen-Synchroton, * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY * * This library is free software; you can redistribute it and/or modify @@ -19,10 +19,10 @@ */ package org.dcache.nfs.v3; -import org.dcache.auth.Subjects; import org.dcache.nfs.ExportTable; import org.dcache.nfs.nfsstat; import org.dcache.nfs.ChimeraNFSException; +import org.dcache.nfs.util.UnixSubjects; import org.dcache.nfs.v3.xdr.LOOKUP3res; import org.dcache.nfs.v3.xdr.WRITE3resfail; import org.dcache.nfs.v3.xdr.RMDIR3resok; @@ -300,7 +300,7 @@ public CREATE3res NFSPROC3_CREATE_3(RpcCall call$, CREATE3args arg1) { if (newAttr != null) { fmode = newAttr.mode.mode.value.value | Stat.S_IFREG; if( newAttr.uid.set_it || newAttr.gid.set_it) { - actualSubject = Subjects.of(newAttr.uid.uid.value.value, newAttr.gid.gid.value.value); + actualSubject = UnixSubjects.toSubject(newAttr.uid.uid.value.value, newAttr.gid.gid.value.value); } } inode = fs.create(parent, Stat.Type.REGULAR, path, actualSubject, fmode); @@ -600,7 +600,7 @@ public MKDIR3res NFSPROC3_MKDIR_3(RpcCall call$, MKDIR3args arg1) { if (attr != null) { mode = attr.mode.mode.value.value | Stat.S_IFDIR; if( attr.uid.set_it || attr.gid.set_it) { - actualSubject = Subjects.of(attr.uid.uid.value.value, attr.gid.gid.value.value); + actualSubject = UnixSubjects.toSubject(attr.uid.uid.value.value, attr.gid.gid.value.value); } } diff --git a/core/src/main/java/org/dcache/nfs/v4/CompoundContext.java b/core/src/main/java/org/dcache/nfs/v4/CompoundContext.java index a94d501ed..5b328e00e 100644 --- a/core/src/main/java/org/dcache/nfs/v4/CompoundContext.java +++ b/core/src/main/java/org/dcache/nfs/v4/CompoundContext.java @@ -21,6 +21,8 @@ import java.net.InetSocketAddress; import java.security.Principal; + +import com.sun.security.auth.UnixNumericUserPrincipal; import org.dcache.nfs.ChimeraNFSException; import org.dcache.nfs.ExportTable; import org.dcache.nfs.v4.xdr.nfs_resop4; @@ -32,7 +34,6 @@ import java.util.Optional; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosPrincipal; -import org.dcache.auth.UidPrincipal; import org.dcache.nfs.vfs.Inode; import org.dcache.nfs.status.BadStateidException; import org.dcache.nfs.status.NoFileHandleException; @@ -320,7 +321,7 @@ private Principal principalOf(final RpcCall call) { if(call.getCredential().type() == RpcAuthType.RPCGSS_SEC) { type = KerberosPrincipal.class; } else { - type = UidPrincipal.class; + type = UnixNumericUserPrincipal.class; } return call.getCredential().getSubject().getPrincipals().stream() diff --git a/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java b/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java index bdf09a06f..01278a91b 100644 --- a/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java +++ b/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java @@ -30,7 +30,7 @@ import java.util.Set; import java.util.function.Function; import javax.security.auth.Subject; -import org.dcache.auth.Subjects; + import org.dcache.nfs.ChimeraNFSException; import org.dcache.nfs.ExportTable; import org.dcache.nfs.FsExport; @@ -53,6 +53,8 @@ import static com.google.common.collect.Lists.newArrayList; import static org.dcache.nfs.vfs.AclCheckable.Access; +import static org.dcache.nfs.util.UnixSubjects.*; + /** * A decorated {@code VirtualFileSystem} that builds a Pseudo file system * on top of an other file system based on export rules. @@ -176,13 +178,13 @@ public int access(Inode inode, int mode) throws IOException { public Inode create(Inode parent, Stat.Type type, String path, Subject subject, int mode) throws IOException { Subject effectiveSubject = checkAccess(parent, ACE4_ADD_FILE); - if (subject != null && Subjects.isRoot(effectiveSubject)) { + if (subject != null && isRootSubject(effectiveSubject)) { effectiveSubject = subject; } if (inheritUidGid(parent)) { Stat s = _inner.getattr(parent); - effectiveSubject = Subjects.of(s.getUid(), s.getGid()); + effectiveSubject = toSubject(s.getUid(), s.getGid()); } return pushExportIndex(parent, _inner.create(parent, type, path, effectiveSubject, mode)); @@ -229,7 +231,7 @@ public Inode link(Inode parent, Inode link, String path, Subject subject) throws Subject effectiveSubject = checkAccess(parent, ACE4_ADD_FILE); if (inheritUidGid(parent)) { Stat s = _inner.getattr(parent); - effectiveSubject = Subjects.of(s.getUid(), s.getGid()); + effectiveSubject = toSubject(s.getUid(), s.getGid()); } return pushExportIndex(parent, _inner.link(parent, link, path, effectiveSubject)); } @@ -247,13 +249,13 @@ public DirectoryStream list(Inode inode, byte[] verifier, long cookie) throws IO @Override public Inode mkdir(Inode parent, String path, Subject subject, int mode) throws IOException { Subject effectiveSubject = checkAccess(parent, ACE4_ADD_SUBDIRECTORY); - if (subject != null && Subjects.isRoot(effectiveSubject)) { + if (subject != null && isRootSubject(effectiveSubject)) { effectiveSubject = subject; } if (inheritUidGid(parent)) { Stat s = _inner.getattr(parent); - effectiveSubject = Subjects.of(s.getUid(), s.getGid()); + effectiveSubject = toSubject(s.getUid(), s.getGid()); } return pushExportIndex(parent, _inner.mkdir(parent, path, effectiveSubject, mode)); } @@ -318,7 +320,7 @@ public Inode symlink(Inode parent, String path, String link, Subject subject, in Subject effectiveSubject = checkAccess(parent, ACE4_ADD_FILE); if (inheritUidGid(parent)) { Stat s = _inner.getattr(parent); - effectiveSubject = Subjects.of(s.getUid(), s.getGid()); + effectiveSubject = toSubject(s.getUid(), s.getGid()); } return pushExportIndex(parent, _inner.symlink(parent, path, link, effectiveSubject, mode)); } @@ -460,8 +462,8 @@ private Subject checkAccess(Inode inode, Stat stat, int requestedMask, boolean s return effectiveSubject; } - if (Subjects.isNobody(_subject) || export.hasAllSquash() || (!export.isTrusted() && Subjects.isRoot(_subject))) { - effectiveSubject = Subjects.of(export.getAnonUid(), export.getAnonGid()); + if (isNobodySubject(_subject) || export.hasAllSquash() || (!export.isTrusted() && isRootSubject(_subject))) { + effectiveSubject = toSubject(export.getAnonUid(), export.getAnonGid()); } if (export.checkAcls()) { @@ -508,12 +510,12 @@ private int unixToAccessmask(Subject subject, Stat stat) { boolean isDir = (mode & Stat.S_IFDIR) == Stat.S_IFDIR; int fromUnixMask; - if (Subjects.isRoot(subject)) { + if (isRootSubject(subject)) { fromUnixMask = Acls.toAccessMask(Acls.RBIT | Acls.WBIT | Acls.XBIT, isDir, true); fromUnixMask |= ACE4_WRITE_OWNER; - } else if (Subjects.hasUid(subject, stat.getUid())) { + } else if (hasUid(subject, stat.getUid())) { fromUnixMask = Acls.toAccessMask(mode >> BIT_MASK_OWNER_OFFSET, isDir, true); - } else if (Subjects.hasGid(subject, stat.getGid())) { + } else if (hasGid(subject, stat.getGid())) { fromUnixMask = Acls.toAccessMask(mode >> BIT_MASK_GROUP_OFFSET, isDir, false); } else { fromUnixMask = Acls.toAccessMask(mode >> BIT_MASK_OTHER_OFFSET, isDir, false); diff --git a/core/src/test/java/org/dcache/nfs/util/UnixSubjectsTest.java b/core/src/test/java/org/dcache/nfs/util/UnixSubjectsTest.java new file mode 100644 index 000000000..347bfeee8 --- /dev/null +++ b/core/src/test/java/org/dcache/nfs/util/UnixSubjectsTest.java @@ -0,0 +1,108 @@ +package org.dcache.nfs.util; + +import com.sun.security.auth.UnixNumericGroupPrincipal; +import com.sun.security.auth.UnixNumericUserPrincipal; +import org.junit.Test; + +import javax.security.auth.Subject; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.LongStream; + +import static org.hamcrest.MatcherAssert.*; + +import static org.hamcrest.Matchers.*; +import static org.dcache.nfs.util.UnixSubjects.*; + +public class UnixSubjectsTest { + + @Test + public void shouldBeRootIfSubjectHasUidZero() { + Subject subject = toSubject(0, 0); + assertThat(isRootSubject(subject), is(true)); + } + + @Test + public void shouldNotBeRootIfSubjectHasUidZero() { + Subject subject = toSubject(1, 1); + assertThat(isRootSubject(subject), is(false)); + } + + @Test + public void shouldBeNobodyIfNoUid() { + Subject subject = new Subject(false, + Set.of(new UnixNumericGroupPrincipal(1, true)), + Set.of(), + Set.of()); + + assertThat(isNobodySubject(subject), is(true)); + } + + @Test + public void shouldContainDesiredUid() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(hasUid(subject, 1), is(true)); + } + + @Test + public void shouldContainDesiredGid() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(hasGid(subject, 2), is(true)); + assertThat(hasGid(subject, 4), is(true)); + assertThat(hasGid(subject, 5), is(true)); + } + + @Test + public void shouldNotContainOtherGid() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(hasGid(subject, 7), is(false)); + } + + @Test + public void shouldNotContainOtherUid() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(hasUid(subject, 7), is(false)); + } + + @Test + public void shouldBuildSubjectWithProvidedUidAndGid() { + Subject subject = toSubject(1, 2); + assertThat(subject.getPrincipals(), hasItem(new UnixNumericUserPrincipal(1))); + assertThat(subject.getPrincipals(), hasItem(new UnixNumericGroupPrincipal(2, true))); + } + + @Test + public void shouldBuildSubjectWithProvidedUidAndGids() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(subject.getPrincipals(), hasItem(new UnixNumericUserPrincipal(1))); + assertThat(subject.getPrincipals(), hasItem(new UnixNumericGroupPrincipal(2, true))); + assertThat(subject.getPrincipals(), hasItem(new UnixNumericGroupPrincipal(4, false))); + assertThat(subject.getPrincipals(), hasItem(new UnixNumericGroupPrincipal(5, false))); + } + + @Test + public void shouldReturnUid() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(getUid(subject), is(1L)); + } + + @Test + public void shouldReturnPrimaryGroup() { + Subject subject = toSubject(1, 2, 4, 5); + assertThat(getPrimaryGid(subject), is(2L)); + } + + @Test + public void shouldReturnSecondaryGroups() { + Subject subject = toSubject(1, 2, 4, 5); + + // hamcrest can't work with primitive arrays. + Set gids = LongStream.of(getSecondaryGids(subject)) + .mapToObj(Long::valueOf) + .collect(Collectors.toSet()); + + assertThat(gids, hasSize(2)); + assertThat(gids, containsInAnyOrder(4L, 5L)); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/dcache/nfs/v4/OperationOPENTest.java b/core/src/test/java/org/dcache/nfs/v4/OperationOPENTest.java index bafdcde07..8bbd41567 100644 --- a/core/src/test/java/org/dcache/nfs/v4/OperationOPENTest.java +++ b/core/src/test/java/org/dcache/nfs/v4/OperationOPENTest.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.OptionalLong; -import org.dcache.auth.Subjects; import org.dcache.nfs.status.BadLayoutException; import org.dcache.nfs.status.GraceException; import org.dcache.nfs.v4.xdr.nfs_fh4; @@ -29,6 +28,9 @@ import org.dcache.nfs.vfs.DirectoryStream; import org.dcache.nfs.vfs.DummyVFS; import org.dcache.nfs.vfs.Stat; + +import javax.security.auth.Subject; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -87,7 +89,7 @@ public void testReturnFhOnOpen() throws Exception { .withCall(generateRpcCall()) .build(); - Inode inode = vfs.create(fsRoot, Stat.Type.REGULAR, "file", Subjects.ROOT, 0644); + Inode inode = vfs.create(fsRoot, Stat.Type.REGULAR, "file", new Subject(), 0644); COMPOUND4res res = execute(context, openArgs); assertEquals("wrong file handle", inode, new Inode(res.resarray.get(3).opgetfh.resok4.object.value)); diff --git a/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java b/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java index b264f6d10..bbb35357c 100644 --- a/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java +++ b/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java @@ -22,6 +22,8 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import com.google.common.primitives.Longs; +import com.sun.security.auth.UnixNumericGroupPrincipal; +import com.sun.security.auth.UnixNumericUserPrincipal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,8 +67,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; -import org.dcache.auth.GidPrincipal; -import org.dcache.auth.UidPrincipal; import org.dcache.nfs.status.NotSuppException; import org.dcache.nfs.status.PermException; import org.dcache.nfs.status.ServerFaultException; @@ -311,11 +311,11 @@ private void setOwnershipAndMode(Path target, Subject subject, int mode) { int uid = -1; int gid = -1; for (Principal principal : subject.getPrincipals()) { - if (principal instanceof UidPrincipal) { - uid = (int) ((UidPrincipal) principal).getUid(); + if (principal instanceof UnixNumericUserPrincipal) { + uid = (int) ((UnixNumericUserPrincipal) principal).longValue(); } - if (principal instanceof GidPrincipal) { - gid = (int) ((GidPrincipal) principal).getGid(); + if (principal instanceof UnixNumericGroupPrincipal) { + gid = (int) ((UnixNumericGroupPrincipal) principal).longValue(); } } diff --git a/core/src/test/java/org/dcache/nfs/vfs/PseudoFsTest.java b/core/src/test/java/org/dcache/nfs/vfs/PseudoFsTest.java index 616857c9b..c87553c22 100644 --- a/core/src/test/java/org/dcache/nfs/vfs/PseudoFsTest.java +++ b/core/src/test/java/org/dcache/nfs/vfs/PseudoFsTest.java @@ -26,7 +26,6 @@ import javax.security.auth.Subject; import com.google.common.primitives.Longs; -import org.dcache.auth.Subjects; import org.dcache.nfs.ExportFile; import org.dcache.nfs.FsExport; import org.dcache.nfs.status.AccessException; @@ -47,6 +46,7 @@ import static org.hamcrest.collection.IsEmptyIterable.emptyIterable; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; +import static org.dcache.nfs.util.UnixSubjects.*; import org.junit.Before; @@ -56,6 +56,8 @@ */ public class PseudoFsTest { + private final Subject ROOT = toSubject(0, 0); + private final static InetSocketAddress localAddress = new InetSocketAddress(31415); private VirtualFileSystem vfs; private ExportFile mockedExportFile; @@ -86,7 +88,7 @@ public void setUp() throws IOException { public void testRootSquash() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -101,14 +103,14 @@ public void testRootSquash() throws IOException { given(mockedExportFile.exports(localAddress.getAddress())).willReturn(Stream.of(export)); pseudoFs = new PseudoFs(vfs, mockedRpc, mockedExportFile); - pseudoFs.create(fsRoot, Stat.Type.REGULAR, "aFile", Subjects.ROOT, 0644); + pseudoFs.create(fsRoot, Stat.Type.REGULAR, "aFile", ROOT, 0644); } @Test public void testNoRootSquash() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -123,14 +125,14 @@ public void testNoRootSquash() throws IOException { given(mockedExportFile.exports(localAddress.getAddress())).willReturn(Stream.of(export)); pseudoFs = new PseudoFs(vfs, mockedRpc, mockedExportFile); - pseudoFs.create(fsRoot, Stat.Type.REGULAR, "aFile", Subjects.ROOT, 0644); + pseudoFs.create(fsRoot, Stat.Type.REGULAR, "aFile", ROOT, 0644); } @Test(expected = AccessException.class) public void testAllSquash() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -152,14 +154,14 @@ public void testAllSquash() throws IOException { given(mockedExportFile.exports(localAddress.getAddress())).willReturn(Stream.of(export)); pseudoFs = new PseudoFs(vfs, mockedRpc, mockedExportFile); - pseudoFs.create(fsRoot, Stat.Type.REGULAR, "aFile", Subjects.ROOT, 0644); + pseudoFs.create(fsRoot, Stat.Type.REGULAR, "aFile", ROOT, 0644); } @Test(expected = PermException.class) public void testAuthFlavorTooWeak() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -183,7 +185,7 @@ public void testAuthFlavorMatch() throws IOException { RpcAuthGss mockedAuthGss = mock(RpcAuthGss.class); given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuthGss.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuthGss.getSubject()).willReturn(ROOT); given(mockedAuthGss.type()).willReturn(RpcAuthType.RPCGSS_SEC); given(mockedAuthGss.getService()).willReturn(RpcGssService.RPC_GSS_SVC_NONE); @@ -210,7 +212,7 @@ public void testAuthFlavorStrong() throws IOException { RpcAuthGss mockedAuthGss = mock(RpcAuthGss.class); given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuthGss.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuthGss.getSubject()).willReturn(ROOT); given(mockedAuthGss.type()).willReturn(RpcAuthType.RPCGSS_SEC); given(mockedAuthGss.getService()).willReturn(RpcGssService.RPC_GSS_SVC_INTEGRITY); @@ -235,7 +237,7 @@ public void testAuthFlavorStrong() throws IOException { public void testAllRoot() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -250,12 +252,12 @@ public void testAllRoot() throws IOException { given(mockedExportFile.getExport(fsRoot.exportIndex(), localAddress.getAddress())).willReturn(export); given(mockedExportFile.exports(localAddress.getAddress())).willReturn(Stream.of(export)); - Subject subject = Subjects.of(17, 17); + Subject subject = toSubject(17, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); pseudoFs = new PseudoFs(vfs, mockedRpc, mockedExportFile); - Inode fileInode = pseudoFs.create(parent, Stat.Type.REGULAR, "aFile", Subjects.ROOT, 0644); + Inode fileInode = pseudoFs.create(parent, Stat.Type.REGULAR, "aFile", ROOT, 0644); Stat stat = pseudoFs.getattr(fileInode); assertEquals("file's owner no propagated", 17, stat.getUid()); @@ -266,12 +268,12 @@ public void testAllRoot() throws IOException { public void testReaddirWithSingleExport() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - vfs.mkdir(fsRoot, "foo", Subjects.ROOT, 0755); - vfs.mkdir(fsRoot, "bar", Subjects.ROOT, 0755); + vfs.mkdir(fsRoot, "foo", ROOT, 0755); + vfs.mkdir(fsRoot, "bar", ROOT, 0755); FsExport export = new FsExport.FsExportBuilder() .rw() .trusted() @@ -301,12 +303,12 @@ public void testReaddirWithSingleExport() throws IOException { public void testLookupOfUnexportedDirectory() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - vfs.mkdir(fsRoot, "foo", Subjects.ROOT, 0755); - vfs.mkdir(fsRoot, "bar", Subjects.ROOT, 0755); + vfs.mkdir(fsRoot, "foo", ROOT, 0755); + vfs.mkdir(fsRoot, "bar", ROOT, 0755); FsExport export = new FsExport.FsExportBuilder() .rw() .trusted() @@ -328,11 +330,11 @@ public void testLookupOfUnexportedDirectory() throws IOException { public void testReaddirBehind() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - vfs.mkdir(fsRoot, "foo", Subjects.ROOT, 0755); + vfs.mkdir(fsRoot, "foo", ROOT, 0755); FsExport export = new FsExport.FsExportBuilder() .rw() .trusted() @@ -364,11 +366,11 @@ public void testReaddirBehind() throws IOException { public void testAccessDenyOnNoExport() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - vfs.mkdir(fsRoot, "foo", Subjects.ROOT, 0755); + vfs.mkdir(fsRoot, "foo", ROOT, 0755); pseudoFs = new PseudoFs(vfs, mockedRpc, mockedExportFile); pseudoFs.getRootInode(); @@ -378,7 +380,7 @@ public void testAccessDenyOnNoExport() throws IOException { public void testAccessDenyOnHighjackedInode() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -398,11 +400,11 @@ public void testAccessDenyOnHighjackedInode() throws IOException { public void testRejectPseudofsModification() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - vfs.mkdir(fsRoot, "foo", Subjects.ROOT, 0755); + vfs.mkdir(fsRoot, "foo", ROOT, 0755); FsExport export = new FsExport.FsExportBuilder() .rw() .trusted() @@ -416,19 +418,19 @@ public void testRejectPseudofsModification() throws IOException { pseudoFs = new PseudoFs(vfs, mockedRpc, mockedExportFile); Inode pseudoRoot = pseudoFs.getRootInode(); - pseudoFs.mkdir(pseudoRoot, "bar", Subjects.ROOT, 0755); + pseudoFs.mkdir(pseudoRoot, "bar", ROOT, 0755); } @Test(expected = AccessException.class) public void testRejectRoExportModification() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - vfs.mkdir(fsRoot, "foo", Subjects.ROOT, 0755); + vfs.mkdir(fsRoot, "foo", ROOT, 0755); FsExport export = new FsExport.FsExportBuilder() .ro() .trusted() @@ -443,19 +445,19 @@ public void testRejectRoExportModification() throws IOException { Inode pseudoRoot = pseudoFs.getRootInode(); Inode exportDir = pseudoFs.lookup(pseudoRoot, "foo"); - pseudoFs.mkdir(exportDir, "bar", Subjects.ROOT, 0755); + pseudoFs.mkdir(exportDir, "bar", ROOT, 0755); } @Test public void testAllowOwnerReadXattr() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(1, 17); + Subject subject = toSubject(1, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); Inode file = vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); vfs.setXattr(file, "xattr1", "value1".getBytes(StandardCharsets.UTF_8), @@ -483,12 +485,12 @@ public void testAllowOwnerReadXattr() throws IOException { public void testRejectReadXattr() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(17, 17); + Subject subject = toSubject(17, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -514,12 +516,12 @@ public void testRejectReadXattr() throws IOException { public void testAllowOwnerListXattrs() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(1, 17); + Subject subject = toSubject(1, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -546,12 +548,12 @@ public void testAllowOwnerListXattrs() throws IOException { public void testRejectListXattrs() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(17, 17); + Subject subject = toSubject(17, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -577,12 +579,12 @@ public void testRejectListXattrs() throws IOException { public void testAllowOwnerRemoveXattr() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(1, 17); + Subject subject = toSubject(1, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -609,12 +611,12 @@ public void testAllowOwnerRemoveXattr() throws IOException { public void testRejectRemoveXattr() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(17, 17); + Subject subject = toSubject(17, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -640,12 +642,12 @@ public void testRejectRemoveXattr() throws IOException { public void testAllowOwnerWriteXattr() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(1, 17); + Subject subject = toSubject(1, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -672,12 +674,12 @@ public void testAllowOwnerWriteXattr() throws IOException { public void testRejectWriteXattr() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.of(1, 1)); + given(mockedAuth.getSubject()).willReturn(toSubject(1, 1)); given(mockedAuth.type()).willReturn(RpcAuthType.UNIX); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); - Subject subject = Subjects.of(17, 17); + Subject subject = toSubject(17, 17); Inode parent = vfs.mkdir(fsRoot, "dir", subject, 0755); vfs.create(parent, Stat.Type.REGULAR, "aFile", subject, 0600); @@ -703,7 +705,7 @@ public void testRejectWriteXattr() throws IOException { public void testUnprivilegedClient() throws IOException { given(mockedTransport.getRemoteSocketAddress()).willReturn(localAddress); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); @@ -726,7 +728,7 @@ public void testPrivilegedClient() throws IOException { InetSocketAddress privilegedClient = new InetSocketAddress(314); given(mockedTransport.getRemoteSocketAddress()).willReturn(privilegedClient); - given(mockedAuth.getSubject()).willReturn(Subjects.ROOT); + given(mockedAuth.getSubject()).willReturn(ROOT); given(mockedRpc.getTransport()).willReturn(mockedTransport); given(mockedRpc.getCredential()).willReturn(mockedAuth); diff --git a/core/src/test/java/org/dcache/nfs/vfs/VfsCacheTest.java b/core/src/test/java/org/dcache/nfs/vfs/VfsCacheTest.java index d611932ba..e42c61cbc 100644 --- a/core/src/test/java/org/dcache/nfs/vfs/VfsCacheTest.java +++ b/core/src/test/java/org/dcache/nfs/vfs/VfsCacheTest.java @@ -1,6 +1,5 @@ package org.dcache.nfs.vfs; -import org.dcache.auth.Subjects; import org.dcache.nfs.v4.xdr.nfs4_prot; import org.junit.Before; import org.junit.Test; @@ -13,7 +12,7 @@ public class VfsCacheTest { - private Subject subject = Subjects.of(0, 1, 2, 3); + private Subject subject = new Subject(); private VirtualFileSystem vfs; private VfsCache vfsCache; diff --git a/pom.xml b/pom.xml index 682fb1134..92d7e621b 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,7 @@ org.dcache oncrpc4j-core - 3.0.3 + 3.1.0 com.hazelcast