Skip to content

Commit

Permalink
Add baseDomain to user
Browse files Browse the repository at this point in the history
  • Loading branch information
humphd committed Mar 7, 2023
1 parent 9fdd304 commit cfc7716
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 54 deletions.
11 changes: 6 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [Development](#development)
- [System prerequisites](#system-prerequisites)
- [Dev environment set up](#dev-environment-set-up)
- [SAML Accounts to use in Dev](#saml-accounts-to-use-in-dev)
- [`.env` and `./dev-secrets/*`](#env-and-dev-secrets)
- [Usage](#usage)
- [Pull requests](#pull-requests)
Expand Down Expand Up @@ -62,11 +63,11 @@ $ npm run db:studio

Our IDP is configured with a few accounts that exist for testing, the usernames and passwords to use are as follows:

| user | pass |
| ----------- | --------- |
| user1 | user1pass |
| user2 | user2pass |
| lippersheyh | telescope |
| user | pass |
| -------- | --------- |
| user1 | user1pass |
| user2 | user2pass |
| han.solo | starchart |

They can be configured in `./config/simplesamlphp-users`

Expand Down
24 changes: 20 additions & 4 deletions app/components/dns-record/form.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import { AddIcon, InfoIcon } from '@chakra-ui/icons';
import { Button, Input, Select, Textarea, Tooltip, VStack } from '@chakra-ui/react';
import {
Button,
Input,
InputGroup,
InputRightAddon,
Select,
Textarea,
Tooltip,
VStack,
} from '@chakra-ui/react';
import { Form } from '@remix-run/react';

import { useUser } from '~/utils';
import FormField from './form-field';

interface DnsRecordFormProps {
typeError?: string; // Error for 'Type' field
}

export default function DnsRecordForm({ typeError }: DnsRecordFormProps) {
const user = useUser();

return (
<Form className="domain-form" method="post">
<VStack maxW="xl" spacing="2">
<FormField label="Domain Name" isRequired={true}>
<Input name="name" />
<Tooltip label="Enter domain name for the DNS Record">
<FormField label="Record Name" isRequired={true}>
<InputGroup>
<Input name="name" />
<InputRightAddon children={`.${user.baseDomain}`} />
</InputGroup>
<Tooltip label="Enter a name for the DNS Record: name">
<InfoIcon color="#d9d9d9" fontSize="xl" />
</Tooltip>
</FormField>
Expand Down
80 changes: 50 additions & 30 deletions app/models/user.server.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
import type { User } from '@prisma/client';

import { prisma } from '~/db.server';
import logger from '~/lib/logger.server';

export type { User } from '@prisma/client';
import type { User as PrismaUser } from '@prisma/client';

export async function getUserByUsername(username: User['username']) {
return prisma.user.findUnique({ where: { username } });
export interface User extends PrismaUser {
// the base domain to use for all DNS records created by this user
// (e.g., jsmith.starchart.com with records using *.jsmith.starchart.com)
baseDomain: string;
}

export async function createUser(
username: User['username'],
firstName: User['firstName'],
lastName: User['lastName'],
email: User['email']
) {
return prisma.user.create({
data: {
username,
firstName,
lastName,
email,
},
});
/**
* Remove invalid/unwanted characters from a username. In the case
* of faculty/admins, we will have a `.` in the username, which we
* don't want to use as part of domain names.
* @param username The user's username (e.g., `jsmith` or `john.smith`)
*/
function cleanUsername(username: PrismaUser['username']) {
return username.replace(/\./g, '');
}

export async function updateUserByUsername(
username: User['username'],
firstName?: User['firstName'],
lastName?: User['lastName'],
email?: User['email']
/**
* Create the domain for a user, using their username
* @param username The user's username (e.g., `jsmith` or `john.smith`)
* @returns string jsmith -> jsmith.starchart.com
*/
function buildUserBaseDomain(username: PrismaUser['username']) {
return `${cleanUsername(username)}.${process.env.ROOT_DOMAIN}`;
}

export async function getUserByUsername(username: PrismaUser['username']) {
const user = await prisma.user.findUnique({ where: { username } });
if (!user) {
return null;
}

// Decorate with the user's base domain as well
return { ...user, baseDomain: buildUserBaseDomain(username) };
}

/**
* Check whether or not a user exists already with the given username
* @param username
* @returns boolean
*/
export async function checkUsernameExists(username: PrismaUser['username']) {
const user = await prisma.user.findUnique({ where: { username } });
return user !== null;
}

export async function createUser(
username: PrismaUser['username'],
firstName: PrismaUser['firstName'],
lastName: PrismaUser['lastName'],
email: PrismaUser['email']
) {
return prisma.user.update({
where: { username },
logger.info(`Creating new user ${username}`);
return prisma.user.create({
data: {
username,
firstName,
Expand All @@ -40,7 +64,3 @@ export async function updateUserByUsername(
},
});
}

export async function deleteUserByUsername(username: User['username']) {
return prisma.user.delete({ where: { username } });
}
10 changes: 7 additions & 3 deletions app/routes/__index/domains/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { redirect, typedjson, useTypedActionData } from 'remix-typedjson';

import DnsRecordForm from '~/components/dns-record/form';
import { createRecord } from '~/models/record.server';
import { requireUsername } from '~/session.server';
import { requireUser } from '~/session.server';

import type { ActionArgs } from '@remix-run/node';
import type { ZodError } from 'zod';
Expand All @@ -16,7 +16,7 @@ function errorForField(error: ZodError, field: string) {
}

export const action = async ({ request }: ActionArgs) => {
const username = await requireUsername(request);
const user = await requireUser(request);

// Create a Zod schema for validation
// Optional is not needed as we get '' if nothing is entered
Expand All @@ -40,9 +40,13 @@ export const action = async ({ request }: ActionArgs) => {
});
}

// Update the DNS record's name with the user's full base domain
const { data } = newDnsRecordParams;
data.name = `${data.name}.${user.baseDomain}`;

// Create a pending record... for now
// This will most likely be replaced by some other logic
const record = await createRecord({ username, ...newDnsRecordParams.data, status: 'pending' });
const record = await createRecord({ username: user.username, ...data, status: 'pending' });
return redirect(`/domains/${record.id}`);
};

Expand Down
10 changes: 4 additions & 6 deletions app/routes/login/callback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ActionArgs } from '@remix-run/node';
import { createUserSession } from '~/session.server';
import { parseLoginResponse } from '~/lib/saml.server';
import { redirect } from '@remix-run/node';
import { createUser, getUserByUsername } from '~/models/user.server';
import { createUser, checkUsernameExists } from '~/models/user.server';

/* This is the post route that the SAML response is bound to. It comes back as formData.
We attempt to extract the SAML response into a json format that we can then use:
Expand Down Expand Up @@ -56,12 +56,10 @@ export const action = async ({ request }: ActionArgs) => {
}
const returnTo = relayState ? relayState : '/';
const username = samlResponse.attributes.sAMAccountName;
// get or create user
let user = await getUserByUsername(username);

// If not create one
if (!user) {
user = await createUser(
// If this user has never logged in before, add to our system
if (!(await checkUsernameExists(username))) {
await createUser(
username,
samlResponse.attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'],
samlResponse.attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'],
Expand Down
12 changes: 6 additions & 6 deletions config/simplesamlphp-users.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@
'sAMAccountName'=> 'user2',
'http://schemas.microsoft.com/identity/claims/displayname' => 'Galileo Galilei',
),
'lippersheyh:telescope' => array(
'han.solo:starchart' => array(
'uid' => array('2'),
'eduPersonAffiliation' => array('group2'),
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress' => 'hansolo@myseneca.ca',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress' => 'han.solo@myseneca.ca',
'email' => '[email protected]',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname' => 'Hans',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname' => 'Lippershey',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'=> 'hansolo@myseneca.ca',
'sAMAccountName'=> 'hansolo',
'http://schemas.microsoft.com/identity/claims/displayname' => 'Hans Lippershey',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname' => 'Solo',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'=> 'han.solo@myseneca.ca',
'sAMAccountName'=> 'han.solo',
'http://schemas.microsoft.com/identity/claims/displayname' => 'Hans Solo',

),
),
Expand Down

0 comments on commit cfc7716

Please sign in to comment.