fix: Enhance dataset controllers with user authentication checks and improve mail configuration
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 59s

This commit is contained in:
Kaimbacher 2025-11-13 11:02:00 +01:00
commit d44d08abcd
7 changed files with 204 additions and 48 deletions

View file

@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v3
- run: echo "The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "The workflow is now ready to test your code on the runner."
- name: List files in the repository:
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- run: echo "This job's status is ${{ job.status }}."

View file

@ -85,14 +85,15 @@ export default class MailSettingsController {
}
try {
await mail.send((message) => {
message
// .from(Config.get('mail.from.address'))
.from('tethys@geosphere.at')
.to(userEmail)
.subject('Test Email')
.html('<p>If you received this email, the email configuration seems to be correct.</p>');
});
await mail.send(
(message) => {
message
// .from(Config.get('mail.from.address'))
.from('tethys@geosphere.at')
.to(userEmail)
.subject('Test Email')
.html('<p>If you received this email, the email configuration seems to be correct.</p>');
});
return response.json({ success: true, message: 'Test email sent successfully' });
// return response.flash('Test email sent successfully!', 'message').redirect().back();

View file

@ -188,10 +188,16 @@ export default class DatasetsController {
}
}
public async approve({ request, inertia, response }: HttpContext) {
public async approve({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// $dataset = Dataset::with('user:id,login')->findOrFail($id);
const dataset = await Dataset.findOrFail(id);
const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) {
@ -217,7 +223,7 @@ export default class DatasetsController {
});
}
public async approveUpdate({ request, response }: HttpContext) {
public async approveUpdate({ request, response, auth }: HttpContext) {
const approveDatasetSchema = vine.object({
reviewer_id: vine.number(),
});
@ -230,7 +236,11 @@ export default class DatasetsController {
throw error;
}
const id = request.param('id');
const dataset = await Dataset.findOrFail(id);
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) {
@ -261,10 +271,15 @@ export default class DatasetsController {
}
}
public async reject({ request, inertia, response }: HttpContext) {
public async reject({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
// .preload('titles')
// .preload('descriptions')
.preload('user', (builder) => {
@ -292,9 +307,14 @@ export default class DatasetsController {
public async rejectUpdate({ request, response, auth }: HttpContext) {
const authUser = auth.user!;
if (!authUser) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const id = request.param('id');
const dataset = await Dataset.query()
.where('id', id)
.where('editor_id', authUser.id) // Ensure the user is the editor of the dataset
.preload('user', (builder) => {
builder.select('id', 'login', 'email');
})
@ -377,9 +397,14 @@ export default class DatasetsController {
public async publish({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.preload('titles')
.preload('authors')
// .preload('persons', (builder) => {
@ -408,7 +433,7 @@ export default class DatasetsController {
});
}
public async publishUpdate({ request, response }: HttpContext) {
public async publishUpdate({ request, response, auth }: HttpContext) {
const publishDatasetSchema = vine.object({
publisher_name: vine.string().trim(),
});
@ -420,7 +445,12 @@ export default class DatasetsController {
throw error;
}
const id = request.param('id');
const dataset = await Dataset.findOrFail(id);
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).firstOrFail();
// let test = await Dataset.getMax('publish_id');
// const maxPublishId = await Database.from('documents').max('publish_id as max_publish_id').first();
@ -446,10 +476,16 @@ export default class DatasetsController {
}
}
public async rejectToReviewer({ request, inertia, response }: HttpContext) {
public async rejectToReviewer({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.preload('reviewer', (builder) => {
builder.select('id', 'login', 'email');
})
@ -475,9 +511,14 @@ export default class DatasetsController {
public async rejectToReviewerUpdate({ request, response, auth }: HttpContext) {
const authUser = auth.user!;
if (!authUser) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const id = request.param('id');
const dataset = await Dataset.query()
.where('id', id)
.where('editor_id', authUser.id) // Ensure the user is the editor of the dataset
.preload('reviewer', (builder) => {
builder.select('id', 'login', 'email');
})
@ -558,10 +599,16 @@ export default class DatasetsController {
.toRoute('editor.dataset.list');
}
public async doiCreate({ request, inertia }: HttpContext) {
public async doiCreate({ request, inertia, auth, response }: HttpContext) {
const id = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.where('id', id)
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.preload('titles')
.preload('descriptions')
// .preload('identifier')
@ -572,11 +619,18 @@ export default class DatasetsController {
});
}
public async doiStore({ request, response }: HttpContext) {
public async doiStore({ request, response, auth }: HttpContext) {
const dataId = request.param('publish_id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Load dataset with minimal required relationships
const dataset = await Dataset.query().where('publish_id', dataId).firstOrFail();
const dataset = await Dataset.query()
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
.where('publish_id', dataId)
.firstOrFail();
const prefix = process.env.DATACITE_PREFIX || '';
const base_domain = process.env.BASE_DOMAIN || '';
@ -658,9 +712,17 @@ export default class DatasetsController {
public async show({}: HttpContext) {}
public async edit({ request, inertia, response }: HttpContext) {
public async edit({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const datasetQuery = Dataset.query().where('id', id);
// Check if user is authenticated
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Prefilter by both id AND editor_id to ensure user has permission to edit
const datasetQuery = Dataset.query().where('id', id).where('editor_id', user.id);
datasetQuery
.preload('titles', (query) => query.orderBy('id', 'asc'))
.preload('descriptions', (query) => query.orderBy('id', 'asc'))
@ -677,6 +739,7 @@ export default class DatasetsController {
query.orderBy('sort_order', 'asc'); // Sort by sort_order column
});
// This will throw 404 if editor_id does not match logged in user
const dataset = await datasetQuery.firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) {
@ -750,11 +813,16 @@ export default class DatasetsController {
});
}
public async update({ request, response, session }: HttpContext) {
public async update({ request, response, session, auth }: HttpContext) {
// Get the dataset id from the route parameter
const datasetId = request.param('id');
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Retrieve the dataset and load its existing files
const dataset = await Dataset.findOrFail(datasetId);
const dataset = await Dataset.query().where('id', datasetId).where('editor_id', user.id).firstOrFail();
await dataset.load('files');
let trx: TransactionClientContract | null = null;
@ -763,7 +831,7 @@ export default class DatasetsController {
trx = await db.transaction();
// const user = (await User.find(auth.user?.id)) as User;
// await this.createDatasetAndAssociations(user, request, trx);
const dataset = await Dataset.findOrFail(datasetId);
// const dataset = await Dataset.findOrFail(datasetId);
// save the licenses
const licenses: number[] = request.input('licenses', []);
@ -949,10 +1017,15 @@ export default class DatasetsController {
}
}
public async categorize({ inertia, request, response }: HttpContext) {
public async categorize({ inertia, request, response, auth }: HttpContext) {
const id = request.param('id');
// Check if user is authenticated
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Preload dataset and its "collections" relation
const dataset = await Dataset.query().where('id', id).preload('collections').firstOrFail();
const dataset = await Dataset.query().where('id', id).where('editor_id', user.id).preload('collections').firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) {
// session.flash('errors', 'Invalid server state!');
@ -980,10 +1053,15 @@ export default class DatasetsController {
});
}
public async categorizeUpdate({ request, response, session }: HttpContext) {
public async categorizeUpdate({ request, response, session, auth }: HttpContext) {
// Get the dataset id from the route parameter
const id = request.param('id');
const dataset = await Dataset.query().preload('files').where('id', id).firstOrFail();
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Retrieve the dataset and load its existing files
const dataset = await Dataset.query().preload('files').where('id', id).where('editor_id', user.id).firstOrFail();
const validStates = ['editor_accepted', 'rejected_reviewer'];
if (!validStates.includes(dataset.server_state)) {
@ -1188,7 +1266,7 @@ export default class DatasetsController {
}
// return cache.getDomDocument();
const xmlDocument : XMLBuilder | null = await serializer.toXmlDocument();
const xmlDocument: XMLBuilder | null = await serializer.toXmlDocument();
return xmlDocument;
}
}

View file

@ -824,13 +824,20 @@ export default class DatasetController {
};
// public async release({ params, view }) {
public async release({ request, inertia, response }: HttpContext) {
public async release({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.preload('user', (builder) => {
builder.select('id', 'login');
})
.where('account_id', user.id) // Only fetch if user owns it
.where('id', id)
.firstOrFail();
@ -851,9 +858,20 @@ export default class DatasetController {
});
}
public async releaseUpdate({ request, response }: HttpContext) {
public async releaseUpdate({ request, response, auth }: HttpContext) {
const id = request.param('id');
const dataset = await Dataset.query().preload('files').where('id', id).firstOrFail();
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
const dataset = await Dataset.query()
.preload('files')
.where('id', id)
.where('account_id', user.id) // Only fetch if user owns it
.firstOrFail();
const validStates = ['inprogress', 'rejected_editor'];
if (!validStates.includes(dataset.server_state)) {
@ -933,7 +951,15 @@ export default class DatasetController {
public async edit({ request, inertia, response, auth }: HttpContext) {
const id = request.param('id');
const datasetQuery = Dataset.query().where('id', id);
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Prefilter by both id AND account_id
const datasetQuery = Dataset.query().where('id', id).where('account_id', user.id); // Only fetch if user owns it
datasetQuery
.preload('titles', (query) => query.orderBy('id', 'asc'))
.preload('descriptions', (query) => query.orderBy('id', 'asc'))
@ -949,8 +975,9 @@ export default class DatasetController {
.preload('files', (query) => {
query.orderBy('sort_order', 'asc'); // Sort by sort_order column
});
// This will throw 404 if dataset doesn't exist OR user doesn't own it
const dataset = await datasetQuery.firstOrFail();
const validStates = ['inprogress', 'rejected_editor'];
if (!validStates.includes(dataset.server_state)) {
// session.flash('errors', 'Invalid server state!');
@ -1014,11 +1041,30 @@ export default class DatasetController {
});
}
public async update({ request, response, session }: HttpContext) {
public async update({ request, response, session, auth }: HttpContext) {
// Get the dataset id from the route parameter
const datasetId = request.param('id');
// Retrieve the dataset and load its existing files
const dataset = await Dataset.findOrFail(datasetId);
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to update a dataset.', 'error').redirect().toRoute('app.login.show');
}
// Prefilter by both id AND account_id
const dataset = await Dataset.query()
.where('id', datasetId)
.where('account_id', user.id) // Only fetch if user owns it
.firstOrFail();
// // Check if the authenticated user is the owner of the dataset
// if (dataset.account_id !== user.id) {
// return response
// .flash(`Unauthorized access. You are not the owner of dataset with id ${id}.`, 'error')
// .redirect()
// .toRoute('dataset.list');
// }
await dataset.load('files');
// Accumulate the size of the already related files
// const preExistingFileSize = dataset.files.reduce((acc, file) => acc + file.fileSize, 0);
@ -1442,16 +1488,26 @@ export default class DatasetController {
}
}
public async delete({ request, inertia, response, session }: HttpContext) {
public async delete({ request, inertia, response, session, auth }: HttpContext) {
const id = request.param('id');
const user = auth.user;
// Check if user is authenticated
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
try {
// This will throw 404 if dataset doesn't exist OR user doesn't own it
const dataset = await Dataset.query()
.preload('user', (builder) => {
builder.select('id', 'login');
})
.where('id', id)
.where('account_id', user.id) // Only fetch if user owns it
.preload('files')
.firstOrFail();
const validStates = ['inprogress', 'rejected_editor'];
if (!validStates.includes(dataset.server_state)) {
// session.flash('errors', 'Invalid server state!');
@ -1476,9 +1532,27 @@ export default class DatasetController {
}
}
public async deleteUpdate({ params, session, response }: HttpContext) {
public async deleteUpdate({ params, session, response, auth }: HttpContext) {
try {
const dataset = await Dataset.query().where('id', params.id).preload('files').firstOrFail();
const user = auth.user;
if (!user) {
return response.flash('You must be logged in to edit a dataset.', 'error').redirect().toRoute('app.login.show');
}
// This will throw 404 if dataset doesn't exist OR user doesn't own it
const dataset = await Dataset.query()
.where('id', params.id)
.where('account_id', user.id) // Only fetch if user owns it
.preload('files')
.firstOrFail();
// // Check if the authenticated user is the owner of the dataset
// if (dataset.account_id !== user.id) {
// return response
// .flash(`Unauthorized access. You are not the owner of dataset with id ${params.id}.`, 'error')
// .redirect()
// .toRoute('dataset.list');
// }
const validStates = ['inprogress', 'rejected_editor'];
if (validStates.includes(dataset.server_state)) {

View file

@ -16,7 +16,7 @@ const mailConfig = defineConfig({
host: env.get('SMTP_HOST', ''),
port: env.get('SMTP_PORT'),
secure: false,
// ignoreTLS: true,
ignoreTLS: true,
requireTLS: false,
/**

View file

@ -87,8 +87,10 @@ import BaseIcon from '@/Components/BaseIcon.vue';
import { MapOptions } from './MapOptions';
import { LayerOptions, LayerMap } from './LayerOptions';
import { MapService } from '@/Stores/map.service';
import { ZoomControlComponent } from './zoom.component.vue';
import { DrawControlComponent } from './draw.component.vue';
// import ZoomControlComponent from '@/Components/Map/zoom.component.vue';
// import DrawControlComponent from '@/Components/Map/draw.component.vue';
import ZoomControlComponent from './zoom.component.vue';
import DrawControlComponent from './draw.component.vue';
import { Coverage } from '@/Dataset';
import { canvas } from 'leaflet/src/layer/vector/Canvas';
import { svg } from 'leaflet/src/layer/vector/SVG';
@ -137,7 +139,7 @@ const DEFAULT_BASE_LAYER_ATTRIBUTION = '&copy; <a target="_blank" href="http://o
BaseIcon,
},
})
export class MapComponent extends Vue {
export default class MapComponent extends Vue {
@Prop()
public mapId: string;
@ -301,7 +303,7 @@ export class MapComponent extends Vue {
}
}
}
export default MapComponent;
// export default MapComponent;
</script>
<style scoped>

View file

@ -933,6 +933,7 @@ import FileUploadComponent from '@/Components/FileUpload.vue';
import { MapOptions } from '@/Components/Map/MapOptions';
import { LatLngBoundsExpression } from 'leaflet';
import { LayerOptions } from '@/Components/Map/LayerOptions';
import BaseIcon from '@/Components/BaseIcon.vue';
import {
mdiImageText,
mdiArrowLeftBoldOutline,