Skip to content

Commit

Permalink
rquota: add stub for rquota service
Browse files Browse the repository at this point in the history
Motivation:
Add server stub for remote quota management protocol.
Added test case for permission check.

Result:
Servers can provide rquota support if needed.

Fixes: #133
Acked-by: Lea Morschel
Target: master
  • Loading branch information
kofemann committed Sep 10, 2024
1 parent c4cb94d commit 0597235
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 2 deletions.
4 changes: 2 additions & 2 deletions rquota/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<version>0.26.0-SNAPSHOT</version>
</parent>

<groupId>org.dcache</groupId>
<artifactId>rquota</artifactId>
<packaging>jar</packaging>
<description>Remote quota protocol implementation</description>
Expand Down Expand Up @@ -43,7 +42,8 @@
</dependency>
<dependency>
<groupId>org.dcache</groupId>
<artifactId>oncrpc4j-core</artifactId>
<artifactId>nfs4j-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

Expand Down
125 changes: 125 additions & 0 deletions rquota/src/main/java/org/dcache/rquota/QuotaSvc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2024 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.rquota;

import static org.dcache.rquota.QuotaVfs.GROUP_QUOTA;
import static org.dcache.rquota.QuotaVfs.USER_QUOTA;
import org.dcache.nfs.util.SubjectHolder;
import org.dcache.nfs.util.UnixSubjects;
import org.dcache.oncrpc4j.rpc.RpcCall;
import org.dcache.rquota.xdr.ext_getquota_args;
import org.dcache.rquota.xdr.ext_setquota_args;
import org.dcache.rquota.xdr.getquota_args;
import org.dcache.rquota.xdr.getquota_rslt;
import org.dcache.rquota.xdr.qr_status;
import org.dcache.rquota.xdr.rquotaServerStub;
import org.dcache.rquota.xdr.setquota_args;
import org.dcache.rquota.xdr.setquota_rslt;
import org.slf4j.Logger;

import javax.security.auth.Subject;

public class QuotaSvc extends rquotaServerStub {

private final static Logger LOGGER = org.slf4j.LoggerFactory.getLogger(QuotaSvc.class);

private final QuotaVfs _qfs;

public QuotaSvc(QuotaVfs _qfs) {
this._qfs = _qfs;
}

@Override
public getquota_rslt RQUOTAPROC_GETQUOTA_1(RpcCall call$, getquota_args arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public getquota_rslt RQUOTAPROC_GETACTIVEQUOTA_1(RpcCall call$, getquota_args arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public setquota_rslt RQUOTAPROC_SETQUOTA_1(RpcCall call$, setquota_args arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public setquota_rslt RQUOTAPROC_SETACTIVEQUOTA_1(RpcCall call$, setquota_args arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public getquota_rslt RQUOTAPROC_GETQUOTA_2(RpcCall call$, ext_getquota_args arg1) {
var r = new getquota_rslt();

if (!canQuery(call$.getCredential().getSubject(), arg1.gqa_id, arg1.gqa_type)) {
r.status = qr_status.Q_EPERM;
return r;
}

r.status = qr_status.Q_OK;
r.gqr_rquota = _qfs.getQuota(arg1.gqa_id, arg1.gqa_type);
return r;
}

@Override
public getquota_rslt RQUOTAPROC_GETACTIVEQUOTA_2(RpcCall call$, ext_getquota_args arg1) {
var r = new getquota_rslt();

if (!canQuery(call$.getCredential().getSubject(), arg1.gqa_id, arg1.gqa_type)) {
r.status = qr_status.Q_EPERM;
return r;
}

r.status = qr_status.Q_OK;
r.gqr_rquota = _qfs.getQuota(arg1.gqa_id, arg1.gqa_type);
return r;
}

@Override
public setquota_rslt RQUOTAPROC_SETQUOTA_2(RpcCall call$, ext_setquota_args arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public setquota_rslt RQUOTAPROC_SETACTIVEQUOTA_2(RpcCall call$, ext_setquota_args arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}

/**
* Check if the given subject can query the quota for the given id and type.
*
* @param subject the subject to check
* @param id the id to check
* @param type the type to check
* @return true if the subject can query the quota, false otherwise
*/
private boolean canQuery(Subject subject, int id, int type) {

boolean canQuery = UnixSubjects.isRootSubject(subject) ||
(type == USER_QUOTA && UnixSubjects.hasUid(subject, id)) ||
(type == GROUP_QUOTA && UnixSubjects.hasGid(subject, id));

LOGGER.debug("Request by {} to query quota for id {} and type {}: {}",
new SubjectHolder(subject), id, type, canQuery ? "granted" : "denied");
return canQuery;
}
}
47 changes: 47 additions & 0 deletions rquota/src/main/java/org/dcache/rquota/QuotaVfs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 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.rquota;

import org.dcache.rquota.xdr.rquota;

/**
* Interface for querying quotas.
*/
public interface QuotaVfs {

/**
* User quota type.
*/
int USER_QUOTA = 0;

/**
* Group quota type.
*/
int GROUP_QUOTA = 1;

/**
* Get the quota for the given id.
*
* @param id numeric id of user or group to get quota for.
* @param type type of quota to get, either {@link #USER_QUOTA} or {@link #GROUP_QUOTA}.
* @return the quota for the given subject
*/
rquota getQuota(int id, int type);
}
79 changes: 79 additions & 0 deletions rquota/src/test/java/org/dcache/rquota/QuotaSvcTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.dcache.rquota;

import static org.dcache.rquota.QuotaVfs.GROUP_QUOTA;
import static org.dcache.rquota.QuotaVfs.USER_QUOTA;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import org.dcache.nfs.util.UnixSubjects;
import org.dcache.oncrpc4j.rpc.RpcAuth;
import org.dcache.oncrpc4j.rpc.RpcCall;
import org.dcache.rquota.xdr.ext_getquota_args;
import org.dcache.rquota.xdr.getquota_rslt;
import org.dcache.rquota.xdr.qr_status;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

public class QuotaSvcTest {

private QuotaVfs quotaVfs;
private QuotaSvc quotaSvc;
private RpcCall call;
private RpcAuth auth;

@Before
public void setUp() {
quotaVfs = mock(QuotaVfs.class);
quotaSvc = new QuotaSvc(quotaVfs);
call = mock(RpcCall.class);
auth = mock(RpcAuth.class);
}

@Test
public void testGetQuotaWrongUser() {
ext_getquota_args args = new ext_getquota_args();
args.gqa_id = 1;
args.gqa_type = USER_QUOTA;

Mockito.when(call.getCredential()).thenReturn(auth);
Mockito.when(auth.getSubject()).thenReturn(UnixSubjects.toSubject(2, 2));
getquota_rslt result = quotaSvc.RQUOTAPROC_GETQUOTA_2(call, args);
assertEquals(qr_status.Q_EPERM, result.status);
}

@Test
public void testGetQuotaWrongGroup() {
ext_getquota_args args = new ext_getquota_args();
args.gqa_id = 1;
args.gqa_type = GROUP_QUOTA;

Mockito.when(call.getCredential()).thenReturn(auth);
Mockito.when(auth.getSubject()).thenReturn(UnixSubjects.toSubject(2, 2));
getquota_rslt result = quotaSvc.RQUOTAPROC_GETQUOTA_2(call, args);
assertEquals(qr_status.Q_EPERM, result.status);
}

@Test
public void testGetUserQuota() {
ext_getquota_args args = new ext_getquota_args();
args.gqa_id = 1;
args.gqa_type = USER_QUOTA;

Mockito.when(call.getCredential()).thenReturn(auth);
Mockito.when(auth.getSubject()).thenReturn(UnixSubjects.toSubject(1, 1));
getquota_rslt result = quotaSvc.RQUOTAPROC_GETQUOTA_2(call, args);
assertEquals(qr_status.Q_OK, result.status);
}

@Test
public void testGetGroupQuota() {
ext_getquota_args args = new ext_getquota_args();
args.gqa_id = 1;
args.gqa_type = GROUP_QUOTA;

Mockito.when(call.getCredential()).thenReturn(auth);
Mockito.when(auth.getSubject()).thenReturn(UnixSubjects.toSubject(1, 1));
getquota_rslt result = quotaSvc.RQUOTAPROC_GETQUOTA_2(call, args);
assertEquals(qr_status.Q_OK, result.status);
}
}

0 comments on commit 0597235

Please sign in to comment.