Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Session type parameter for TypeInfo, Lists and Context #8570

Merged
merged 19 commits into from
May 18, 2023
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
5 changes: 5 additions & 0 deletions .changeset/add-cookie-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/core': minor
---

Adds `cookieName` as an option for `statelessSessions`
5 changes: 5 additions & 0 deletions .changeset/more-session-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/core': minor
---

Adds `Session` type parameter to generated `TypeInfo`, `Lists` and `Context` types, and propagates that type to access control and hooks
30 changes: 14 additions & 16 deletions examples/auth/keystone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ const sessionMaxAge = 60 * 60 * 24 * 30;

// withAuth is a function we can use to wrap our base configuration
const { withAuth } = createAuth({
// this is the list that contains items people can sign in as
// this is the list that contains our users
listKey: 'User',

// an identity field is typically a username or email address
identityField: 'email',
// an identity field, typically a username or an email address
identityField: 'name',

// a secret field must be a password type field
// a secret field must be a password field type
secretField: 'password',

// initFirstItem enables the "First User" experience, this will add an interface form
Expand All @@ -34,7 +34,7 @@ const { withAuth } = createAuth({
// see https://keystonejs.com/docs/config/auth#init-first-item for more
initFirstItem: {
// the following fields are used by the "Create First User" form
fields: ['name', 'email', 'password'],
fields: ['name', 'password'],

// the following fields are configured by default for this item
itemData: {
Expand All @@ -43,18 +43,10 @@ const { withAuth } = createAuth({
},
},

// add isAdmin to the session data(required by isAccessAllowed)
// add isAdmin to the session data
sessionData: 'isAdmin',
});

// you can find out more at https://keystonejs.com/docs/apis/session#session-api
const session = statelessSessions({
// an maxAge option controls how long session cookies are valid for before they expire
maxAge: sessionMaxAge,
// an session secret is used to encrypt cookie data (should be an environment variable)
secret: sessionSecret,
});

export default withAuth(
config({
db: {
Expand All @@ -65,12 +57,18 @@ export default withAuth(
...fixPrismaPath,
},
lists,
session,
ui: {
// only admins can view the AdminUI
isAccessAllowed: ({ session }) => {
return session?.data?.isAdmin;
return session?.data?.isAdmin ?? false;
},
},
// you can find out more at https://keystonejs.com/docs/apis/session#session-api
session: statelessSessions({
// the maxAge option controls how long session cookies are valid for before they expire
maxAge: sessionMaxAge,
// the session secret is used to encrypt cookie data
secret: sessionSecret,
}),
})
);
41 changes: 1 addition & 40 deletions examples/auth/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
type User {
id: ID!
name: String
email: String
password: PasswordState
isAdmin: Boolean
}
Expand All @@ -22,8 +21,6 @@ input UserWhereInput {
OR: [UserWhereInput!]
NOT: [UserWhereInput!]
id: IDFilter
name: StringFilter
password: PasswordFilter
isAdmin: BooleanFilter
}

Expand All @@ -38,46 +35,13 @@ input IDFilter {
not: IDFilter
}

input StringFilter {
equals: String
in: [String!]
notIn: [String!]
lt: String
lte: String
gt: String
gte: String
contains: String
startsWith: String
endsWith: String
not: NestedStringFilter
}

input NestedStringFilter {
equals: String
in: [String!]
notIn: [String!]
lt: String
lte: String
gt: String
gte: String
contains: String
startsWith: String
endsWith: String
not: NestedStringFilter
}

input PasswordFilter {
isSet: Boolean!
}

input BooleanFilter {
equals: Boolean
not: BooleanFilter
}

input UserOrderByInput {
id: OrderDirection
name: OrderDirection
isAdmin: OrderDirection
}

Expand All @@ -88,7 +52,6 @@ enum OrderDirection {

input UserUpdateInput {
name: String
email: String
password: String
isAdmin: Boolean
}
Expand All @@ -100,7 +63,6 @@ input UserUpdateArgs {

input UserCreateInput {
name: String
email: String
password: String
isAdmin: Boolean
}
Expand All @@ -118,7 +80,7 @@ type Mutation {
deleteUser(where: UserWhereUniqueInput!): User
deleteUsers(where: [UserWhereUniqueInput!]!): [User]
endSession: Boolean!
authenticateUserWithPassword(email: String!, password: String!): UserAuthenticationWithPasswordResult
authenticateUserWithPassword(name: String!, password: String!): UserAuthenticationWithPasswordResult
createInitialUser(data: CreateInitialUserInput!): UserAuthenticationWithPasswordSuccess!
}

Expand All @@ -135,7 +97,6 @@ type UserAuthenticationWithPasswordFailure {

input CreateInitialUserInput {
name: String
email: String
password: String
}

Expand Down
5 changes: 2 additions & 3 deletions examples/auth/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ generator client {

model User {
id String @id @default(cuid())
name String @default("")
email String @unique @default("")
password String?
name String @unique @default("")
password String
isAdmin Boolean @default(false)
}
36 changes: 15 additions & 21 deletions examples/auth/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { list } from '@keystone-6/core';
import { allowAll } from '@keystone-6/core/access';
import { allowAll, denyAll } from '@keystone-6/core/access';
import { text, checkbox, password } from '@keystone-6/core/fields';
import type { Lists } from '.keystone/types';

Expand All @@ -14,17 +14,11 @@ type Session = {
};
};

function hasSession({ session }: { session: Session | undefined }) {
function hasSession({ session }: { session?: Session }) {
return Boolean(session);
}

function isAdminOrSameUser({
session,
item,
}: {
session: Session | undefined;
item: Lists.User.Item;
}) {
function isAdminOrSameUser({ session, item }: { session?: Session; item: Lists.User.Item }) {
// you need to have a session to do this
if (!session) return false;

Expand All @@ -35,7 +29,7 @@ function isAdminOrSameUser({
return session.itemId === item.id;
}

function isAdminOrSameUserFilter({ session }: { session: Session | undefined }) {
function isAdminOrSameUserFilter({ session }: { session?: Session }) {
// you need to have a session to do this
if (!session) return false;

Expand All @@ -50,7 +44,7 @@ function isAdminOrSameUserFilter({ session }: { session: Session | undefined })
};
}

function isAdmin({ session }: { session: Session | undefined }) {
function isAdmin({ session }: { session?: Session }) {
// you need to have a session to do this
if (!session) return false;

Expand Down Expand Up @@ -89,18 +83,16 @@ export const lists: Lists = {
hideDelete: args => !isAdmin(args),
listView: {
// the default columns that will be displayed in the list view
initialColumns: ['name', 'email', 'isAdmin'],
initialColumns: ['name', 'isAdmin'],
},
},
fields: {
// the user's name, publicly visible
name: text({ validation: { isRequired: true } }),

// the user's email address, used as the identity field for authentication
// the user's name, used as the identity field for authentication
// should not be publicly visible
//
// we use isIndexed to enforce this email is unique
email: text({
// we use isIndexed to enforce names are unique
// that may not suitable for your application
name: text({
access: {
// only the respective user, or an admin can read this field
read: isAdminOrSameUser,
Expand All @@ -120,17 +112,19 @@ export const lists: Lists = {
// should not be publicly visible
password: password({
access: {
read: isAdminOrSameUser, // TODO: is this required?
read: denyAll, // TODO: is this required?
update: isAdminOrSameUser,
},
validation: {
isRequired: true,
},
ui: {
itemView: {
// don't show this field if it isn't relevant
fieldMode: args => (isAdminOrSameUser(args) ? 'edit' : 'hidden'),
},
listView: {
// TODO: ?
fieldMode: args => (isAdmin(args) ? 'read' : 'hidden'),
fieldMode: 'hidden', // TODO: is this required?
},
},
}),
Expand Down
18 changes: 10 additions & 8 deletions examples/custom-output-paths/my-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@ type ResolvedPostUpdateInput = {
};

export declare namespace Lists {
export type Post = import('@keystone-6/core').ListConfig<Lists.Post.TypeInfo, any>;
export type Post<Session = any> = import('@keystone-6/core').ListConfig<Lists.Post.TypeInfo<Session>, any>;
namespace Post {
export type Item = import('./node_modules/.myprisma/client').Post;
export type TypeInfo = {
export type TypeInfo<Session = any> = {
key: 'Post';
isSingleton: false;
fields: 'id' | 'title' | 'content' | 'publishDate'
Expand All @@ -166,23 +166,25 @@ export declare namespace Lists {
create: ResolvedPostCreateInput;
update: ResolvedPostUpdateInput;
};
all: __TypeInfo;
all: __TypeInfo<Session>;
};
}
}
export type Context = import('@keystone-6/core/types').KeystoneContext<TypeInfo>;
export type Context<Session = any> = import('@keystone-6/core/types').KeystoneContext<TypeInfo<Session>>;
export type Config<Session = any> = import('@keystone-6/core/types').KeystoneConfig<TypeInfo<Session>>;

export type TypeInfo = {
export type TypeInfo<Session = any> = {
lists: {
readonly Post: Lists.Post.TypeInfo;
};
prisma: import('./node_modules/.myprisma/client').PrismaClient;
session: Session;
};

type __TypeInfo = TypeInfo;
type __TypeInfo<Session = any> = TypeInfo<Session>;

export type Lists = {
[Key in keyof TypeInfo['lists']]?: import('@keystone-6/core').ListConfig<TypeInfo['lists'][Key], any>
export type Lists<Session = any> = {
[Key in keyof TypeInfo['lists']]?: import('@keystone-6/core').ListConfig<TypeInfo<Session>['lists'][Key], any>
} & Record<string, import('@keystone-6/core').ListConfig<any, any>>;

export {}
Loading