package elw.dao;
import base.pattern.Result;
import com.google.common.io.InputSupplier;
import elw.dao.ctx.*;
import elw.dao.rest.RestEnrollment;
import elw.dao.rest.RestEnrollmentSummary;
import elw.dao.rest.RestSolution;
import elw.vo.*;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class QueriesSecure implements Queries {
public static final String MODEL_KEY = "elw_queries";
private final QueriesImpl queries;
private final Auth auth;
public Auth getAuth() { return auth; }
private final List<String> visibleGroupIds = new ArrayList<String>(1);
public List<String> getVisibleGroupIds() {
return Collections.unmodifiableList(visibleGroupIds);
}
private final List<String> visibleEnrIds = new ArrayList<String>(2);
public List<String> getVisibleEnrIds() {
return Collections.unmodifiableList(visibleEnrIds);
}
private final List<String> visibleCourseIds = new ArrayList<String>(2);
public List<String> getVisibleCourseIds() {
return Collections.unmodifiableList(visibleCourseIds);
}
public QueriesSecure(QueriesImpl queries, Auth auth) {
this.auth = auth;
this.queries = queries;
}
public void precacheVisible() {
visibleGroupIds.addAll(groupIds());
visibleEnrIds.addAll(enrollmentIds());
visibleCourseIds.addAll(courseIds());
}
public RestEnrollmentSummary restScores(
final String enrId,
final Collection<String> studentIds
) {
if (auth.isAdm()) {
return queries.restScores(enrId, studentIds);
}
if (!enrollmentIds().contains(enrId)) {
return null;
}
final TreeSet<String> studIds = new TreeSet<String>();
if (auth.isStud()) {
studIds.add(auth.getStudent().getId());
}
if (studentIds != null) {
studIds.retainAll(studentIds);
}
return queries.restScores(enrId, studIds);
}
public RestEnrollment restEnrollment(
final String enrId,
final String sourceAddress
) {
if (auth.isAdm()) {
return queries.restEnrollment(enrId, sourceAddress);
}
if (!enrollmentIds().contains(enrId)) {
return null;
}
final RestEnrollment enrollment =
queries.restEnrollment(enrId, sourceAddress);
final Group group =
group(enrollment.getGroupId());
for (RestEnrollment.RestIndexEntry entry :
enrollment.getIndex().values()) {
final TreeMap<String, Version> versions =
new TreeMap<String, Version>();
// hide all versions for not yet open tasks
if (entry.getClassFrom().isStarted()) {
// hide all versions which are not shared
for (Map.Entry<String, Version> verEntry :
entry.getTask().getVersions().entrySet()) {
if (verEntry.getValue().isShared()) {
versions.entrySet().add(verEntry);
}
}
// hide all versions which are not assigned
final Version version = Nav.resolveVersion(
entry.getTask(),
entry.getVerAnchor(),
entry.getVerStep(),
group.getStudents().keySet(),
auth.getStudent().getId()
);
versions.put(version.getId(), version);
}
entry.getTask().setVersions(versions);
// hide version anchors and steps
entry.setVerStep(-1);
entry.setVerAnchor(-1);
}
return enrollment;
}
public Map<String,RestSolution> restSolutions(
final String enrId,
final SolutionFilter filter
) {
if (auth.isAdm()) {
return queries.restSolutions(enrId, filter);
}
if (!enrollmentIds().contains(enrId)) {
return null;
}
// TODO a bit of null-safety here won't hurt anyone
return queries.restSolutions(enrId, new SolutionFilter() {
public boolean preAllows(CtxStudent ctxStudent) {
final boolean sameStudent =
ctxStudent.student.equals(auth.getStudent());
return sameStudent && filter.preAllows(ctxStudent);
}
public boolean allows(CtxSolution ctxSolution) {
// funnel call to the original filter,
// preAllows() did the job already
return filter.allows(ctxSolution);
}
});
}
public RestSolution restSolution(
final String enrollmentId,
final String solutionId,
final StudentFilter outer
) {
if (auth.isAdm()) {
return queries.restSolution(
enrollmentId, solutionId, outer
);
}
if (!enrollmentIds().contains(enrollmentId)) {
return null;
}
final StudentFilter inner = secureStudentFilter(outer);
final RestSolution restSolution = queries.restSolution(
enrollmentId, solutionId, inner
);
return restSolution;
}
protected StudentFilter secureStudentFilter(final StudentFilter outer) {
return new StudentFilter() {
public boolean allows(Student student) {
final boolean outerAllowed =
outer == null || outer.allows(student);
if (auth.isAdm()) {
return outerAllowed;
}
return outerAllowed && authStudent(student);
}
};
}
protected boolean authStudent(Student student) {
return student.equals(auth.getStudent());
}
public CtxResolutionState resolveSlot(
final String enrollmentId,
final String couchId,
final StudentFilter outer
) {
if (auth.isAdm()) {
return queries.resolveSlot(enrollmentId, couchId, outer);
}
if (!enrollmentIds().contains(enrollmentId)) {
return CtxResolutionState.failed(
couchId,
"enrollment access denied"
);
}
return queries.resolveSlot(
enrollmentId, couchId, secureStudentFilter(outer)
);
}
public CtxSolution resolveSolution(
final String enrollmentId,
final String couchId,
final StudentFilter outer
) {
if (auth.isAdm()) {
return queries.resolveSolution(enrollmentId, couchId, outer);
}
if (!enrollmentIds().contains(enrollmentId)) {
return null;
}
return queries.resolveSolution(
enrollmentId, couchId, secureStudentFilter(outer)
);
}
public boolean createSolution(
final CtxSlot ctxSlot,
final Solution solution,
final String contentType,
final InputSupplier<InputStream> inputSupplier
) {
// LATER admin should not be able to create solutions
// at least until impersonation is more or less functional
if (auth.isAdm()) {
return queries.createSolution(
ctxSlot, solution,
contentType, inputSupplier
);
}
if (!enrollmentIds().contains(ctxSlot.enr.getId())) {
return false;
}
if (!authStudent(ctxSlot.student)) {
return false;
}
if (!ctxSlot.open()) {
return false;
}
//noinspection SimplifiableIfStatement
if (!ctxSlot.writable(ctxSlot.slot, stateForSlot(ctxSlot))) {
return false;
}
return queries.createSolution(
ctxSlot, solution,
contentType, inputSupplier
);
}
public Admin adminSome(String login) {
if (auth.isAdm()) {
return queries.adminSome(login);
}
return null;
}
public List<Admin> admins() {
if (auth.isAdm()) {
return queries.admins();
}
return Collections.emptyList();
}
public List<Attachment> attachments(final CtxSlot ctxSlot) {
if (auth.isAdm()) {
return queries.attachments(ctxSlot);
}
if (!enrollmentIds().contains(ctxSlot.enr.getId())) {
return null;
}
if (!ctxSlot.open()) {
return null;
}
//noinspection SimplifiableIfStatement
if (!ctxSlot.readable(ctxSlot.slot, stateForSlot(ctxSlot))) {
return null;
}
return queries.attachments(
ctxSlot
);
}
protected CtxTask.StateForSlot stateForSlot(final CtxSlot ctxSlot) {
return new CtxTask.StateForSlot() {
public State getState(FileSlot slot) {
final CtxSlot ctxApproveSlot = ctxSlot.slot(slot);
return ctxApproveSlot.state(solutions(ctxApproveSlot));
}
};
}
public Attachment attachment(Ctx ctxVer, String slotId, String id) {
return null; // TODO review
}
public SortedMap<String, List<Solution>> solutions(Ctx ctx) {
return null; // TODO review
}
public Score score(CtxSolution ctx) {
return null; // TODO review
}
public List<Solution> solutions(CtxSlot ctx) {
return null; // TODO review
}
public Score score(CtxSolution ctx, Long stamp) {
return null; // TODO review
}
public Result createFile(Ctx ctx, FileSlot slot, FileBase file, InputSupplier<? extends InputStream> inputSupplier, String contentType) {
return null; // TODO review
}
public List<? extends FileBase> files(String scope, Ctx ctx, FileSlot slot) {
return null; // TODO review
}
public FileBase file(String scope, Ctx ctx, FileSlot slot, String id) {
return null; // TODO review
}
public InputSupplier<InputStream> attachmentInput(
final @Nonnull CtxAttachment ctxAttachment,
final @Nonnull String fileName
) {
if (auth.isAdm()) {
return queries.attachmentInput(ctxAttachment, fileName);
}
if (!enrollmentIds().contains(ctxAttachment.enr.getId())) {
return null;
}
if (!ctxAttachment.open()) {
return null;
}
//noinspection SimplifiableIfStatement
if (!ctxAttachment.readable(ctxAttachment.slot, stateForSlot(ctxAttachment))) {
return null;
}
return queries.attachmentInput(
ctxAttachment, fileName
);
}
public InputSupplier<InputStream> solutionInput(
final @Nonnull CtxSolution ctxSolution,
final @Nonnull String fileName
) {
if (auth.isAdm()) {
return queries.solutionInput(ctxSolution, fileName);
}
if (!enrollmentIds().contains(ctxSolution.enr.getId())) {
return null;
}
if (!authStudent(ctxSolution.student)) {
return null;
}
if (!ctxSolution.open()) {
return null;
}
//noinspection SimplifiableIfStatement
if (!ctxSolution.readable(ctxSolution.slot, stateForSlot(ctxSolution))) {
return null;
}
return queries.solutionInput(
ctxSolution, fileName
);
}
public List<Course> courses() {
return queries.courses();
}
public List<String> courseIds() {
// check auth-scope cache
final List<String> preCached = visibleCourseIds;
if (!preCached.isEmpty()) {
return preCached;
}
// admin sees anything
if (auth.isAdm()) {
final List<String> courseIds =
queries.courseIds();
return courseIds;
}
// any other user sees courses he/she is enrolled to
final List<Enrollment> enrollments =
enrollments();
final List<String> courseIds =
new ArrayList<String>();
for (Enrollment enr : enrollments) {
final String courseId = enr.getCourseId();
if (!courseIds.contains(courseId)) {
courseIds.add(courseId);
}
}
return Collections.unmodifiableList(courseIds);
}
public Course course(String courseId) {
if (!courseIds().contains(courseId)) {
return null;
}
return queries.course(courseId);
}
public List<String> groupIds() {
// check auth-scope cache
final List<String> preCached = visibleGroupIds;
if (!preCached.isEmpty()) {
return preCached;
}
// admin sees anything
if (auth.isAdm()) {
return queries.groupIds();
}
// any other user sees only groups he/she is a student of
final List<String> groupIds = new ArrayList<String>();
if (auth.isStud()) {
groupIds.add(auth.getGroup().getId());
}
return Collections.unmodifiableList(groupIds);
}
public void invalidateCaches() {
if (auth.isAdm()) {
queries.invalidateCaches();
}
}
public Group group(String groupId) {
if (groupIds().contains(groupId)) {
return queries.group(groupId);
}
return null;
}
public List<Group> groups() {
// admin sees anything
if (auth.isAdm()) {
return queries.groups();
}
// check auth-scope explicit group listing
final List<String> authExplicit = visibleGroupIds;
if (!authExplicit.isEmpty()) {
final List<Group> groups =
new ArrayList<Group>();
for (String groupId : authExplicit) {
groups.add(queries.group(groupId));
}
return Collections.unmodifiableList(groups);
}
// any other user sees only groups he/she is a student of
final List<Group> groups =
new ArrayList<Group>();
groups.add(auth.getGroup());
return Collections.unmodifiableList(groups);
}
public List<String> enrollmentIds() {
// check auth-scope cache
final List<String> preCached = visibleEnrIds;
if (!preCached.isEmpty()) {
return preCached;
}
// admin sees anything
if (auth.isAdm()) {
return queries.enrollmentIds();
}
// any other user sees only enrollments for respective groups
final List<Group> groups = groups();
final List<String> enrollmentIds =
new ArrayList<String>();
for (Group userGroup : groups) {
final List<Enrollment> enrForGroup =
queries.enrollmentsForGroup(userGroup.getId());
for (Enrollment enr : enrForGroup) {
enrollmentIds.add(enr.getId());
}
}
return Collections.unmodifiableList(enrollmentIds);
}
public List<Enrollment> enrollments() {
// admin sees anything
if (auth.isAdm()) {
return queries.enrollments();
}
// check auth-scope cache
final List<String> preCached = visibleEnrIds;
if (!preCached.isEmpty()) {
final List<Enrollment> enrollments =
new ArrayList<Enrollment>();
for (String enrId : preCached) {
enrollments.add(hideVersions(queries.enrollment(enrId)));
}
return Collections.unmodifiableList(enrollments);
}
// any other user sees only enrollments for respective groups
final List<Group> groups = groups();
final List<Enrollment> enrollments =
new ArrayList<Enrollment>();
for (Group userGroup : groups) {
final List<Enrollment> enrForGroup =
queries.enrollmentsForGroup(userGroup.getId());
for (Enrollment enr : enrForGroup) {
enrollments.add(hideVersions(enr));
}
}
return Collections.unmodifiableList(enrollments);
}
protected Enrollment hideVersions(Enrollment enr) {
try {
final Enrollment clone = enr.clone();
for (IndexEntry entry : clone.getIndex().values()) {
entry.setVerAnchor(1);
entry.setVerStep(-1);
}
return clone;
} catch (CloneNotSupportedException e) {
throw new IllegalStateException(e);
}
}
public Enrollment enrollment(String enrId) {
if (auth.isAdm()) {
return queries.enrollment(enrId);
}
if (enrollmentIds().contains(enrId)) {
return hideVersions(queries.enrollment(enrId));
}
return null;
}
public Enrollment enrollmentSome(String enrId) {
if (auth.isAdm()) {
return queries.enrollmentSome(enrId);
}
if (enrollmentIds().contains(enrId)) {
final Enrollment enrSome = queries.enrollmentSome(enrId);
if (enrSome != null) {
return hideVersions( enrSome);
}
}
return null;
}
public List<Enrollment> enrollmentsForGroup(String groupId) {
if (auth.isAdm()) {
return queries.enrollmentsForGroup(groupId);
}
if (!groupIds().contains(groupId)) {
return null;
}
final List<Enrollment> enr = queries.enrollmentsForGroup(groupId);
for (int enrPos = 0; enrPos < enr.size(); enrPos++) {
enr.set(enrPos, hideVersions(enr.get(enrPos)));
}
return enr;
}
public SortedMap<Long, Score> scores(Ctx ctx, FileSlot slot, Solution file) {
return null; // TODO review
}
public long createScore(CtxSolution ctxSolution, Score score) {
return 0; // TODO review
}
public void updateSolution(Solution solution) {
// log something sensible
}
public String fileText(FileBase file, String attachment) throws IOException {
return null; // TODO review
}
public List<String> fileLines(FileBase file, String attachment) throws IOException {
return null; // TODO review
}
}