Skip to content

Commit

Permalink
feat: add disallow registration and anonymous links option
Browse files Browse the repository at this point in the history
Resolves #290, #210, #34
  • Loading branch information
poeti8 committed Aug 9, 2020
1 parent dbc402f commit b229a3a
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 25 deletions.
7 changes: 7 additions & 0 deletions .example.env
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,17 @@ REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=

# Disable registration
DISALLOW_REGISTRATION=false

# Disable anonymous link creation
DISALLOW_ANONYMOUS_LINKS=false

# The daily limit for each user
USER_LIMIT_PER_DAY=50

# Create a cooldown for non-logged in users in minutes
# Would be ignored if DISALLOW_ANONYMOUS_LINKS is set to true
# Set 0 to disable
NON_USER_COOLDOWN=0

Expand Down
11 changes: 9 additions & 2 deletions client/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Router from "next/router";
import useMedia from "use-media";
import Link from "next/link";

import { DISALLOW_REGISTRATION } from "../consts";
import { useStoreState } from "../store";
import styled from "styled-components";
import { RowCenterV } from "./Layout";
Expand Down Expand Up @@ -55,8 +56,14 @@ const Header: FC = () => {
const login = !isAuthenticated && (
<Li>
<Link href="/login">
<ALink href="/login" title="login / signup" forButton>
<Button height={[32, 40]}>Login / Sign up</Button>
<ALink
href="/login"
title={!DISALLOW_REGISTRATION ? "login / signup" : "login"}
forButton
>
<Button height={[32, 40]}>
{!DISALLOW_REGISTRATION ? "Log in / Sign up" : "Log in"}
</Button>
</ALink>
</Link>
</Li>
Expand Down
10 changes: 10 additions & 0 deletions client/consts/consts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import getConfig from "next/config";

const { publicRuntimeConfig } = getConfig();

export const DISALLOW_ANONYMOUS_LINKS =
publicRuntimeConfig.DISALLOW_ANONYMOUS_LINKS === "true";

export const DISALLOW_REGISTRATION =
publicRuntimeConfig.DISALLOW_REGISTRATION === "true";

export enum API {
BAN_LINK = "/api/url/admin/ban",
STATS = "/api/url/stats"
Expand Down
11 changes: 11 additions & 0 deletions client/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from "react";
import Router from "next/router";

import { DISALLOW_ANONYMOUS_LINKS } from "../consts";
import NeedToLogin from "../components/NeedToLogin";
import Extensions from "../components/Extensions";
import LinksTable from "../components/LinksTable";
Expand All @@ -12,6 +14,15 @@ import { useStoreState } from "../store";
const Homepage = () => {
const isAuthenticated = useStoreState(s => s.auth.isAuthenticated);

if (
!isAuthenticated &&
DISALLOW_ANONYMOUS_LINKS &&
typeof window !== "undefined"
) {
Router.push("/login");
return null;
}

return (
<AppWrapper>
<Shortener />
Expand Down
38 changes: 20 additions & 18 deletions client/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Link from "next/link";
import axios from "axios";

import { useStoreState, useStoreActions } from "../store";
import { APIv2, DISALLOW_REGISTRATION } from "../consts";
import { ColCenterV } from "../components/Layout";
import AppWrapper from "../components/AppWrapper";
import { TextInput } from "../components/Input";
Expand All @@ -16,7 +17,6 @@ import { Button } from "../components/Button";
import Text, { H2 } from "../components/Text";
import ALink from "../components/ALink";
import Icon from "../components/Icon";
import { APIv2 } from "../consts";

const LoginForm = styled(Flex).attrs({
as: "form",
Expand Down Expand Up @@ -77,7 +77,7 @@ const LoginPage = () => {
}
}

if (type === "signup") {
if (type === "signup" && !DISALLOW_REGISTRATION) {
setLoading(s => ({ ...s, signup: true }));
try {
await axios.post(APIv2.AuthSignup, { email, password });
Expand Down Expand Up @@ -120,7 +120,7 @@ const LoginPage = () => {
autoFocus
/>
<Text {...label("password")} as="label" mb={2} bold>
Password (min chars: 8):
Password{!DISALLOW_REGISTRATION ? " (min chars: 8)" : ""}:
</Text>
<TextInput
{...password("password")}
Expand All @@ -135,7 +135,7 @@ const LoginPage = () => {
<Flex justifyContent="center">
<Button
flex="1 1 auto"
mr={["8px", 16]}
mr={!DISALLOW_REGISTRATION ? ["8px", 16] : 0}
height={[44, 56]}
onClick={onSubmit("login")}
>
Expand All @@ -146,20 +146,22 @@ const LoginPage = () => {
/>
Log in
</Button>
<Button
flex="1 1 auto"
ml={["8px", 16]}
height={[44, 56]}
color="purple"
onClick={onSubmit("signup")}
>
<Icon
name={loading.signup ? "spinner" : "signup"}
stroke="white"
mr={2}
/>
Sign up
</Button>
{!DISALLOW_REGISTRATION && (
<Button
flex="1 1 auto"
ml={["8px", 16]}
height={[44, 56]}
color="purple"
onClick={onSubmit("signup")}
>
<Icon
name={loading.signup ? "spinner" : "signup"}
stroke="white"
mr={2}
/>
Sign up
</Button>
)}
</Flex>
<Link href="/reset-password">
<ALink
Expand Down
4 changes: 3 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ module.exports = {
DEFAULT_DOMAIN: localEnv && localEnv.DEFAULT_DOMAIN,
RECAPTCHA_SITE_KEY: localEnv && localEnv.RECAPTCHA_SITE_KEY,
GOOGLE_ANALYTICS: localEnv && localEnv.GOOGLE_ANALYTICS,
REPORT_EMAIL: localEnv && localEnv.REPORT_EMAIL
REPORT_EMAIL: localEnv && localEnv.REPORT_EMAIL,
DISALLOW_ANONYMOUS_LINKS: localEnv && localEnv.DISALLOW_ANONYMOUS_LINKS,
DISALLOW_REGISTRATION: localEnv && localEnv.DISALLOW_REGISTRATION
}
};
2 changes: 2 additions & 0 deletions server/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const env = cleanEnv(process.env, {
USER_LIMIT_PER_DAY: num({ default: 50 }),
NON_USER_COOLDOWN: num({ default: 10 }),
DEFAULT_MAX_STATS_PER_LINK: num({ default: 5000 }),
DISALLOW_ANONYMOUS_LINKS: bool({ default: false }),
DISALLOW_REGISTRATION: bool({ default: false }),
CUSTOM_DOMAIN_USE_HTTPS: bool({ default: false }),
JWT_SECRET: str(),
ADMIN_EMAILS: str({ default: "" }),
Expand Down
13 changes: 10 additions & 3 deletions server/handlers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const apikey = authenticate(
);

export const cooldown: Handler = async (req, res, next) => {
if (env.DISALLOW_ANONYMOUS_LINKS) return next();
const cooldownConfig = env.NON_USER_COOLDOWN;
if (req.user || !cooldownConfig) return next();

Expand All @@ -83,6 +84,7 @@ export const cooldown: Handler = async (req, res, next) => {

export const recaptcha: Handler = async (req, res, next) => {
if (env.isDev || req.user) return next();
if (env.DISALLOW_ANONYMOUS_LINKS) return next();
if (!env.RECAPTCHA_SECRET_KEY) return next();

const isReCaptchaValid = await axios({
Expand Down Expand Up @@ -167,7 +169,7 @@ export const changePassword: Handler = async (req, res) => {
.send({ message: "Your password has been changed successfully." });
};

export const generateApiKey = async (req, res) => {
export const generateApiKey: Handler = async (req, res) => {
const apikey = nanoid(40);

redis.remove.user(req.user);
Expand All @@ -181,7 +183,7 @@ export const generateApiKey = async (req, res) => {
return res.status(201).send({ apikey });
};

export const resetPasswordRequest = async (req, res) => {
export const resetPasswordRequest: Handler = async (req, res) => {
const [user] = await query.user.update(
{ email: req.body.email },
{
Expand All @@ -199,7 +201,7 @@ export const resetPasswordRequest = async (req, res) => {
});
};

export const resetPassword = async (req, res, next) => {
export const resetPassword: Handler = async (req, res, next) => {
const { resetPasswordToken } = req.params;

if (resetPasswordToken) {
Expand All @@ -218,3 +220,8 @@ export const resetPassword = async (req, res, next) => {
}
return next();
};

export const signupAccess: Handler = (req, res, next) => {
if (!env.DISALLOW_REGISTRATION) return next();
return res.status(403).send({ message: "Registration is not allowed." });
};
1 change: 1 addition & 0 deletions server/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ router.post(

router.post(
"/signup",
auth.signupAccess,
validators.signup,
asyncHandler(helpers.verify),
asyncHandler(auth.signup)
Expand Down
3 changes: 2 additions & 1 deletion server/routes/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as validators from "../handlers/validators";
import * as helpers from "../handlers/helpers";
import * as link from "../handlers/links";
import * as auth from "../handlers/auth";
import env from "../env";

const router = Router();

Expand All @@ -21,7 +22,7 @@ router.post(
"/",
cors(),
asyncHandler(auth.apikey),
asyncHandler(auth.jwtLoose),
asyncHandler(env.DISALLOW_ANONYMOUS_LINKS ? auth.jwt : auth.jwtLoose),
asyncHandler(auth.recaptcha),
asyncHandler(auth.cooldown),
validators.createLink,
Expand Down

0 comments on commit b229a3a

Please sign in to comment.