Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions api/migrations/34-fix-validate-subscription.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CREATE OR REPLACE FUNCTION ctfnote_private.validate_subscription (topic text)
RETURNS text
AS $$
DECLARE
ctf_id int;
BEGIN
IF ctfnote_private.is_guest () THEN
RETURN gen_random_uuid ();
END IF;
RAISE EXCEPTION 'Please login first';
END;
$$
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER;

GRANT EXECUTE ON FUNCTION ctfnote_private.validate_subscription TO user_anonymous;

25 changes: 25 additions & 0 deletions api/migrations/35-fix-flag-subscription.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
CREATE OR REPLACE FUNCTION ctfnote_private.notify_task_edit ()
RETURNS TRIGGER
AS $$
BEGIN
CASE TG_OP
WHEN 'INSERT' THEN
PERFORM
ctfnote_private.notify ('update', 'ctfs', NEW.ctf_id); RETURN NEW;
WHEN 'UPDATE' THEN
IF NEW.flag <> '' AND (OLD.flag is null or OLD.flag = '') THEN
PERFORM
ctfnote_private.notify ('task-solved', 'tasks', NEW.id);
END IF;
PERFORM
ctfnote_private.notify ('update', 'tasks', NEW.id);
RETURN NEW;
WHEN 'DELETE' THEN
PERFORM
ctfnote_private.notify ('update', 'ctfs', OLD.ctf_id);
RETURN NEW;
RETURN OLD;
END CASE;
END
$$ VOLATILE
LANGUAGE plpgsql;
38 changes: 38 additions & 0 deletions api/migrations/36-split-me-and-new-jwt.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
DROP FUNCTION me;

CREATE OR REPLACE FUNCTION ctfnote.me ()
RETURNS ctfnote.profile
AS $$
SELECT
*
FROM
ctfnote.profile
WHERE
id = ctfnote_private.user_id ()
LIMIT 1;

$$
LANGUAGE SQL
STRICT STABLE;

GRANT EXECUTE ON FUNCTION ctfnote.me () TO user_anonymous;


CREATE OR REPLACE FUNCTION ctfnote.new_token ()
RETURNS ctfnote.jwt
AS $$
DECLARE
user_id int := ctfnote_private.user_id ();
BEGIN
IF user_id IS NOT NULL THEN
RETURN ctfnote_private.new_token (user_id);
ELSE
RETURN NULL;
END IF;
END;
$$ STRICT STABLE
LANGUAGE plpgsql
SECURITY DEFINER;

GRANT EXECUTE ON FUNCTION ctfnote.new_token () TO user_anonymous;

49 changes: 13 additions & 36 deletions front/graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3309,41 +3309,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "MeResponse",
"description": null,
"fields": [
{
"name": "jwt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "Jwt",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "profile",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "Profile",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Mutation",
Expand Down Expand Up @@ -5660,7 +5625,19 @@
"args": [],
"type": {
"kind": "OBJECT",
"name": "MeResponse",
"name": "Profile",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "newToken",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "Jwt",
"ofType": null
},
"isDeprecated": false,
Expand Down
12 changes: 12 additions & 0 deletions front/public/favicon-transparent.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions front/quasar.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ module.exports = configure(function (ctx) {
},

// https://v2.quasar.dev/quasar-cli/prefetch-feature
// preFetch: true,
preFetch: true,

// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli/boot-files
boot: ['apollo'],
boot: ['apollo', 'ctfnote'],

// https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
css: ['app.scss'],
Expand Down Expand Up @@ -109,7 +109,7 @@ module.exports = configure(function (ctx) {
// directives: [],

// Quasar plugins
plugins: ['Notify', 'Dialog'],
plugins: ['Notify', 'Dialog', 'LoadingBar'],
},

// animations: 'all', // --- includes all animations
Expand Down
7 changes: 7 additions & 0 deletions front/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,12 @@ import { defineComponent } from 'vue';

export default defineComponent({
name: 'App',
mounted() {
const loadingscreen = document.getElementById('loadingscreen');
if (loadingscreen) {
loadingscreen.classList.add('fadeout');
window.setTimeout(() => (loadingscreen.style.display = 'none'), 1500);
}
},
});
</script>
2 changes: 1 addition & 1 deletion front/src/boot/apollo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApolloClient /*, createHttpLink */ } from '@apollo/client/core';
import { ApolloClient } from '@apollo/client/core';
import { ApolloClients, provideApolloClient } from '@vue/apollo-composable';
import { boot } from 'quasar/wrappers';
import { getClientOptions } from 'src/apollo';
Expand Down
42 changes: 42 additions & 0 deletions front/src/boot/ctfnote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { boot } from 'quasar/wrappers';
import { ctfnote } from 'src/ctfnote';
import { isLogged } from 'src/ctfnote/me';

export default boot(async ({ router, redirect, urlPath }) => {
router.beforeEach(async (to) => {
const r = await isLogged();
if (!r && !to.meta.public) {
return { name: 'auth-login' };
}
});

router.afterEach((to) => {
if (typeof to.meta.title == 'string') {
const title = to.meta.title;
document.title = title ? `CTFNote - ${title}` : 'CTFNote';
}
});

const url = new URL(urlPath, document.location.origin);
const path = url.hash.slice(1);
const route = router.resolve({ path });
let logged = false;


try {
logged = !!(await ctfnote.auth.refreshJWT());
} catch {
ctfnote.auth.saveJWT(null);
window.location.reload();
}

const prefetchs = [ctfnote.settings.prefetchSettings()];
if (logged) {
prefetchs.push(ctfnote.me.prefetchMe());
}

await Promise.all(prefetchs);
if (!logged && !route.meta?.public) {
redirect({ name: 'auth-login' });
}
});
58 changes: 49 additions & 9 deletions front/src/components/Admin/Registration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,16 @@
</template>

<script lang="ts">
import { Role } from 'src/ctfnote';
import { getAdminSettings, updateSettings } from 'src/ctfnote/settings';
import { Role } from 'src/ctfnote/models';
import ctfnote from 'src/ctfnote';
import { defineComponent, ref, watch } from 'vue';
import PasswordInput from '../Utils/PasswordInput.vue';
import SelectRole from '../Utils/SelectRole.vue';

export default defineComponent({
components: { PasswordInput, SelectRole },
setup() {
const { result: adminSettings } = getAdminSettings();
const { result: adminSettings } = ctfnote.settings.getAdminSettings();

const registrationPassword = ref('');

Expand All @@ -83,6 +83,8 @@ export default defineComponent({
);

return {
wrapNotify: ctfnote.ui.useWrapNotify(),
updateSettings: ctfnote.settings.useUpdateSettings(),
adminSettings,
registrationPassword,
};
Expand All @@ -93,32 +95,70 @@ export default defineComponent({
return this.adminSettings.registrationAllowed ?? true;
},
set(registrationAllowed: boolean) {
void updateSettings({ registrationAllowed });
const opts = {
message: registrationAllowed
? 'Registration enabled'
: 'Registration disabled',
icon: 'lock',
};

void this.wrapNotify(
() => this.updateSettings({ registrationAllowed }),
opts
);
},
},
registrationPasswordAllowed: {
get(): boolean {
return this.adminSettings.registrationPasswordAllowed ?? true;
},
set(registrationPasswordAllowed: boolean) {
void updateSettings({ registrationPasswordAllowed });
const opts = {
message: registrationPasswordAllowed
? 'Registration with password enabled!'
: 'Registration with password disabled!',
icon: 'lock',
};

void this.wrapNotify(
() => this.updateSettings({ registrationPasswordAllowed }),
opts
);
},
},
registrationDefaultRole: {
get(): Role {
return this.adminSettings.registrationDefaultRole ?? Role.UserGuest;
},
set(registrationDefaultRole: Role) {
void updateSettings({ registrationDefaultRole });
const roleName = registrationDefaultRole.slice(5).toLowerCase();
const opts = {
message: `Default role set to ${roleName}!`,
icon: 'lock',
};

void this.wrapNotify(
() => this.updateSettings({ registrationDefaultRole }),
opts
);
},
},
},
watch: {},
methods: {
updateRegistrationPassword() {
void updateSettings({
registrationPassword: this.registrationPassword,
});
const opts = {
message: 'Registration password changed!',
icon: 'lock',
};

void this.wrapNotify(
() =>
this.updateSettings({
registrationPassword: this.registrationPassword,
}),
opts
);
},
},
});
Expand Down
Loading