mirror of
https://codeberg.org/MarkusThielker/next-ory.git
synced 2025-04-11 04:08:43 +00:00
NORY-36: add ory keto to application stack (#38)
This introduces the protection of the admin dashboard using Ory Keto. A script for applying the `admin` role to an identity has been added.
This commit is contained in:
commit
b6ce48e03e
23 changed files with 356 additions and 72 deletions
README.md
authentication
dashboard
docker
README.md
ory-dev
25
README.md
25
README.md
|
@ -1,15 +1,14 @@
|
|||
# Next-Ory
|
||||
|
||||
Get started with ORY authentication quickly and easily.
|
||||
Get started with the Ory stack quickly and easily.
|
||||
|
||||
> [!Warning]
|
||||
> This project is work in progress. There is no guarantee that everything will work as it should and breaking changes in
|
||||
> the future are possible.
|
||||
|
||||
The goal of this project is to create an easy-to-use setup to self-host [Ory Kratos](https://www.ory.sh/kratos)
|
||||
and [Ory Hydra](https://www.ory.sh/hydra). It will contain an authentication UI, implementing all self-service flows for
|
||||
Ory Kratos and Ory Hydra, as well as an admin UI. All UI components are written in NextJS and Typescript, and styled
|
||||
using shadcn/ui and TailwindCSS.
|
||||
The goal of this project is to create an easy-to-use setup to self-host the [Ory](https://www.ory.sh) stack with all its
|
||||
components. It will contain an authentication UI, implementing all self-service flows for Ory Kratos and Ory Hydra, as
|
||||
well as an admin UI. All UI components are written in NextJS and Typescript, and styled using shadcn/ui and TailwindCSS.
|
||||
|
||||
## Getting started
|
||||
|
||||
|
@ -41,6 +40,15 @@ bun install
|
|||
bun run dev
|
||||
```
|
||||
|
||||
To access the admin dashboard, the `identity` has to be a `member` of the `admin` role. (Relation: roles:admin@<
|
||||
identity_id>#member) <br/>
|
||||
The identity ID is displayed on the screen when accessing the dashboard without sufficient permissions. <br/>
|
||||
Use the identity ID to execute the following script with the identity ID as an argument.
|
||||
|
||||
```bash
|
||||
sh docker/ory-dev/keto-make-admin.sh <identity_id>
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
*soon.*
|
||||
|
@ -58,11 +66,10 @@ Hydra. It is implemented in a way, that customizing style and page layout is ver
|
|||
|
||||
## Admin Dashboard
|
||||
|
||||
Right now I am working on the admin dashboard for Ory Kratos. It will provide you with an overview of your instance and
|
||||
let you manage users, OAuth2 applications and more. It is ***work in progress*** and should not be used in anything
|
||||
important as it is not yet protected by Keto permissions but only by a valid Kratos session!
|
||||
Right now I am working on the admin dashboard for all Ory applications. It will provide you with an overview of your
|
||||
instances and let you manage users, OAuth2 applications and more. It is ***work in progress*** and should be handled
|
||||
with caution.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Next-Ory - Authentication
|
||||
|
||||
This directory contains a NextJS 14 (app router) UI Node, implementing all Ory Kratos and Ory Hydra UI flows.
|
||||
This directory contains a NextJS 15 (app router) UI Node, implementing all Ory Kratos and Ory Hydra UI flows.
|
||||
|
||||
## Stack
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ const inter = Inter({ subsets: ['latin'] });
|
|||
const APP_NAME = 'Next Ory';
|
||||
const APP_DEFAULT_TITLE = 'Next Ory';
|
||||
const APP_TITLE_TEMPLATE = `%s | ${APP_DEFAULT_TITLE}`;
|
||||
const APP_DESCRIPTION = 'Get started with ORY authentication quickly and easily.';
|
||||
const APP_DESCRIPTION = 'Get started with Ory authentication quickly and easily.';
|
||||
|
||||
export const metadata = {
|
||||
applicationName: APP_NAME,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Next-Ory - Dashboard
|
||||
|
||||
This directory contains a NextJS 15 (app router) UI Node, implementing the admin dashboard to the ORY Kratos instance.
|
||||
This directory contains a NextJS 15 (app router) UI Node, implementing the admin dashboard for the Ory admin APIs.
|
||||
|
||||
## Stack
|
||||
|
||||
|
|
37
dashboard/src/app/(inside)/layout.tsx
Normal file
37
dashboard/src/app/(inside)/layout.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import '../globals.css';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import React from 'react';
|
||||
import { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar';
|
||||
import { AppSidebar } from '@/components/app-sidebar';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList } from '@/components/ui/breadcrumb';
|
||||
|
||||
export default function InsideLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<SidebarProvider className="max-h-screen min-h-screen">
|
||||
<AppSidebar className="mx-1"/>
|
||||
<SidebarInset className="overflow-hidden p-6 space-y-6">
|
||||
<header className="flex h-4 items-center gap-2">
|
||||
<SidebarTrigger className="-ml-1 p-1"/>
|
||||
<Separator orientation="vertical" className="mr-2 h-4"/>
|
||||
{
|
||||
// TODO: implement dynamic Breadcrumbs
|
||||
}
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem className="hidden md:block">
|
||||
<BreadcrumbLink href="/">
|
||||
Ory Dashboard
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</header>
|
||||
<div className="flex-1 overflow-scroll">
|
||||
{children}
|
||||
</div>
|
||||
</SidebarInset>
|
||||
<Toaster/>
|
||||
</SidebarProvider>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { getHydraMetadataApi, getKratosMetadataApi } from '@/ory/sdk/server';
|
||||
import { getHydraMetadataApi, getKetoMetadataApi, getKratosMetadataApi } from '@/ory/sdk/server';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
|
@ -17,6 +17,7 @@ export default async function RootPage() {
|
|||
const kratosDBStatusData = await fetch(process.env.ORY_KRATOS_ADMIN_URL + '/health/ready');
|
||||
const kratosDBStatus = await kratosDBStatusData.json() as { status: string };
|
||||
|
||||
|
||||
const hydraMetadataApi = await getHydraMetadataApi();
|
||||
|
||||
const hydraVersion = await hydraMetadataApi
|
||||
|
@ -30,13 +31,27 @@ export default async function RootPage() {
|
|||
const hydraDBStatusData = await fetch(process.env.ORY_KRATOS_ADMIN_URL + '/health/ready');
|
||||
const hydraDBStatus = await hydraDBStatusData.json() as { status: string };
|
||||
|
||||
|
||||
const ketoMetadataApi = await getKetoMetadataApi();
|
||||
|
||||
const ketoVersion = await ketoMetadataApi
|
||||
.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => '');
|
||||
|
||||
const ketoStatusData = await fetch(process.env.ORY_KETO_ADMIN_URL + '/health/alive');
|
||||
const ketoStatus = await ketoStatusData.json() as { status: string };
|
||||
|
||||
const ketoDBStatusData = await fetch(process.env.ORY_KETO_ADMIN_URL + '/health/ready');
|
||||
const ketoDBStatus = await ketoDBStatusData.json() as { status: string };
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div>
|
||||
<p className="text-3xl font-bold leading-tight tracking-tight">Software Stack</p>
|
||||
<p className="text-lg font-light">See the list of all applications in your stack</p>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Card className="flex-1">
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
|
@ -73,7 +88,24 @@ export default async function RootPage() {
|
|||
</Badge>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex-1"></div>
|
||||
<Card className="flex-1">
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
Ory Keto
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Version {ketoVersion}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-x-1">
|
||||
<Badge variant={kratosStatus.status === 'ok' ? 'success' : 'destructive'}>
|
||||
Keto {ketoStatus.status.toUpperCase()}
|
||||
</Badge>
|
||||
<Badge variant={kratosStatus.status === 'ok' ? 'success' : 'destructive'}>
|
||||
Database {ketoDBStatus.status.toUpperCase()}
|
||||
</Badge>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex-1"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -6,7 +6,7 @@ import { DataTable } from '@/components/ui/data-table';
|
|||
import { CircleCheck, CircleX, Copy, MoreHorizontal, Trash, UserCheck, UserMinus, UserPen, UserX } from 'lucide-react';
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { FetchIdentityPageProps } from '@/app/user/page';
|
||||
import { FetchIdentityPageProps } from '@/app/(inside)/user/page';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
@ -29,7 +29,7 @@ import {
|
|||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { blockIdentity, deleteIdentity, deleteIdentitySessions, unblockIdentity } from '@/app/user/action';
|
||||
import { blockIdentity, deleteIdentity, deleteIdentitySessions, unblockIdentity } from '@/app/(inside)/user/action';
|
||||
|
||||
interface IdentityDataTableProps {
|
||||
data: Identity[];
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { IdentityDataTable } from '@/app/user/data-table';
|
||||
import { IdentityDataTable } from '@/app/(inside)/user/data-table';
|
||||
import { getIdentityApi } from '@/ory/sdk/server';
|
||||
import { SearchInput } from '@/components/search-input';
|
||||
|
39
dashboard/src/app/(outside)/unauthorised/page.tsx
Normal file
39
dashboard/src/app/(outside)/unauthorised/page.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
'use client';
|
||||
|
||||
import { ErrorDisplay } from '@/components/error';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { kratos, LogoutLink } from '@/ory';
|
||||
import { LogOut } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Session } from '@ory/client';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
|
||||
export default function UnauthorizedPage() {
|
||||
|
||||
const [session, setSession] = useState<Session | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
kratos.toSession()
|
||||
.then((response) => setSession(response.data));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="bg-background min-h-screen p-16">
|
||||
<ErrorDisplay
|
||||
title="Unauthorised"
|
||||
message="You are unauthorised to access this application!"/>
|
||||
|
||||
{
|
||||
session ?
|
||||
<p className="text-xs text-neutral-500">USER ID {session.identity?.id}</p>
|
||||
:
|
||||
<Skeleton className="w-72 h-4"/>
|
||||
}
|
||||
|
||||
<Button className="mt-8 space-x-2" onClick={LogoutLink()}>
|
||||
<LogOut className="h-4 w-4"/>
|
||||
<span>Logout</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -2,20 +2,15 @@ import type { Viewport } from 'next';
|
|||
import { Inter } from 'next/font/google';
|
||||
import './globals.css';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar';
|
||||
import { AppSidebar } from '@/components/app-sidebar';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList } from '@/components/ui/breadcrumb';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
const APP_NAME = 'Next Ory';
|
||||
const APP_DEFAULT_TITLE = 'Next Ory';
|
||||
const APP_TITLE_TEMPLATE = `%s | ${APP_DEFAULT_TITLE}`;
|
||||
const APP_DESCRIPTION = 'Get started with ORY authentication quickly and easily.';
|
||||
const APP_DESCRIPTION = 'Get started with Ory authentication quickly and easily.';
|
||||
|
||||
export const metadata = {
|
||||
applicationName: APP_NAME,
|
||||
|
@ -56,31 +51,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
|
|||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SidebarProvider className="max-h-screen min-h-screen">
|
||||
<AppSidebar className="mx-1"/>
|
||||
<SidebarInset className="overflow-hidden p-6 space-y-6">
|
||||
<header className="flex h-4 items-center gap-2">
|
||||
<SidebarTrigger className="-ml-1 p-1"/>
|
||||
<Separator orientation="vertical" className="mr-2 h-4"/>
|
||||
{
|
||||
// TODO: implement dynamic Breadcrumbs
|
||||
}
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem className="hidden md:block">
|
||||
<BreadcrumbLink href="/">
|
||||
Ory Dashboard
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</header>
|
||||
<div className="flex-1 overflow-scroll">
|
||||
{children}
|
||||
</div>
|
||||
</SidebarInset>
|
||||
<Toaster/>
|
||||
</SidebarProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,11 +3,11 @@ interface ErrorDisplayProps {
|
|||
message: string;
|
||||
}
|
||||
|
||||
export async function ErrorDisplay({ title, message }: ErrorDisplayProps) {
|
||||
export function ErrorDisplay({ title, message }: ErrorDisplayProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<>
|
||||
<p className="text-3xl font-bold leading-tight tracking-tight">{title}</p>
|
||||
<p className="text-lg font-light">{message}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { cookies } from 'next/headers';
|
||||
import { getFrontendApi } from '@/ory/sdk/server';
|
||||
import { getFrontendApi, getPermissionApi } from '@/ory/sdk/server';
|
||||
|
||||
export async function middleware() {
|
||||
export async function middleware(request: NextRequest) {
|
||||
|
||||
const api = await getFrontendApi();
|
||||
const frontendApi = await getFrontendApi();
|
||||
const cookie = await cookies();
|
||||
|
||||
const session = await api
|
||||
const session = await frontendApi
|
||||
.toSession({ cookie: 'ory_kratos_session=' + cookie.get('ory_kratos_session')?.value })
|
||||
.then((response) => response.data)
|
||||
.catch(() => null);
|
||||
|
@ -25,7 +25,40 @@ export async function middleware() {
|
|||
return NextResponse.redirect(url);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
const permissionApi = await getPermissionApi();
|
||||
const isAdmin = await permissionApi.checkPermission({
|
||||
namespace: 'roles',
|
||||
object: 'admin',
|
||||
relation: 'member',
|
||||
subjectId: session!.identity!.id,
|
||||
})
|
||||
.then(({ data: { allowed } }) => {
|
||||
console.log('is_admin', session!.identity!.id, allowed);
|
||||
return allowed;
|
||||
})
|
||||
.catch((response) => {
|
||||
console.log('is_admin', session!.identity!.id, response, 'check failed');
|
||||
return false;
|
||||
});
|
||||
|
||||
if (isAdmin) {
|
||||
if (request.nextUrl.pathname === '/unauthorised') {
|
||||
return redirect('/', 'HAS PERMISSION BUT ACCESSING /unauthorized');
|
||||
}
|
||||
return NextResponse.next();
|
||||
} else {
|
||||
if (request.nextUrl.pathname === '/unauthorised') {
|
||||
return NextResponse.next();
|
||||
}
|
||||
return redirect('/unauthorised', 'MISSING SESSION');
|
||||
}
|
||||
}
|
||||
|
||||
function redirect(path: string, reason: string) {
|
||||
console.log(reason);
|
||||
const url = `${process.env.NEXT_PUBLIC_DASHBOARD_NODE_URL}${path}`;
|
||||
console.log('REDIRECT TO', url);
|
||||
return NextResponse.redirect(url!);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
'use server';
|
||||
|
||||
import { Configuration, FrontendApi, IdentityApi, MetadataApi, OAuth2Api } from '@ory/client';
|
||||
import {
|
||||
Configuration,
|
||||
FrontendApi,
|
||||
IdentityApi,
|
||||
MetadataApi,
|
||||
OAuth2Api,
|
||||
PermissionApi,
|
||||
RelationshipApi,
|
||||
} from '@ory/client';
|
||||
|
||||
|
||||
// ####################################################################################
|
||||
|
@ -92,3 +100,57 @@ const kratosMetadataApi = new MetadataApi(
|
|||
export async function getKratosMetadataApi() {
|
||||
return kratosMetadataApi;
|
||||
}
|
||||
|
||||
|
||||
// ####################################################################################
|
||||
// Relationship API
|
||||
// ####################################################################################
|
||||
|
||||
const relationshipApi = new RelationshipApi(new Configuration(
|
||||
{
|
||||
basePath: process.env.ORY_KETO_ADMIN_URL,
|
||||
baseOptions: {
|
||||
withCredentials: true,
|
||||
},
|
||||
},
|
||||
));
|
||||
|
||||
export async function getRelationshipApi() {
|
||||
return relationshipApi;
|
||||
}
|
||||
|
||||
|
||||
// ####################################################################################
|
||||
// Permission API
|
||||
// ####################################################################################
|
||||
|
||||
const permissionApi = new PermissionApi(new Configuration(
|
||||
{
|
||||
basePath: process.env.ORY_KETO_ADMIN_URL,
|
||||
baseOptions: {
|
||||
withCredentials: true,
|
||||
},
|
||||
},
|
||||
));
|
||||
|
||||
export async function getPermissionApi() {
|
||||
return permissionApi;
|
||||
}
|
||||
|
||||
|
||||
// ####################################################################################
|
||||
// Keto Metadata API
|
||||
// ####################################################################################
|
||||
|
||||
const ketoMetadataApi = new MetadataApi(new Configuration(
|
||||
{
|
||||
basePath: process.env.ORY_KETO_ADMIN_URL,
|
||||
baseOptions: {
|
||||
withCredentials: true,
|
||||
},
|
||||
},
|
||||
));
|
||||
|
||||
export async function getKetoMetadataApi() {
|
||||
return ketoMetadataApi;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Starting as a container
|
||||
|
||||
Starting this project in a container makes testing it really easy. \
|
||||
Starting this project in a container makes testing it really easy.
|
||||
|
||||
```bash
|
||||
# move to the environment you want to start (here development)
|
||||
|
@ -17,7 +17,7 @@ sh ./hydra-test-consent.sh
|
|||
```
|
||||
|
||||
These commands will start up multiple containers in the background.
|
||||
Then continue with starting the authentication UI development server as described in the authentication README.
|
||||
Then continue with starting the authentication UI development server as described in the root README.
|
||||
|
||||
## Services and Ports
|
||||
|
||||
|
@ -28,13 +28,15 @@ If you start up the environment on a remote server, you will need to tunnel the
|
|||
|
||||
| Service | Port (Public) | Description |
|
||||
|----------------|---------------|---------------------------------------------------------------------------|
|
||||
| Console | 4411 (✗) | Admin dashboard for Kratos data management (soon) |
|
||||
| Console | 4000 (✗) | Admin dashboard for Kratos data management (soon) |
|
||||
| Authentication | 3000 (✗) | User interface for authentication and account management (no docker yet) |
|
||||
| ORY Kratos | 4433 (✗) | User management system handling users and self-service flows (Public API) |
|
||||
| ORY Kratos | 4434 (✗) | User management system handling users and self-service flows (Admin API) |
|
||||
| Ory Kratos | 4433 (✗) | User management system handling users and self-service flows (Public API) |
|
||||
| | 4434 (✗) | User management system handling users and self-service flows (Admin API) |
|
||||
| Mailslurper | 4436 (✗) | Mock mailing server (Dashboard) |
|
||||
| Mailslurper | 4437 (✗) | Mock mailing server (API) |
|
||||
| ORY Hydra | 4444 (✗) | OAuth2 and OIDC server connected to Kratos (Public API) |
|
||||
| ORY Hydra | 4445 (✗) | OAuth2 and OIDC server connected to Kratos (Admin API) |
|
||||
| ORY Hydra | 5555 (✗) | Hydra test application to test the consent flow |
|
||||
| | 4437 (✗) | Mock mailing server (API) |
|
||||
| Ory Hydra | 4444 (✗) | OAuth2 and OIDC server connected to Kratos (Public API) |
|
||||
| | 4445 (✗) | OAuth2 and OIDC server connected to Kratos (Admin API) |
|
||||
| | 5555 (✗) | Hydra test application to test the consent flow |
|
||||
| Ory Keto | 4466 (✗) | Read Endpoint for Ory Keto authorization ("Public" API) |
|
||||
| | 4467 (✗) | Write Endpoint for Ory Keto authorization ("Admin" API) |
|
||||
| Postgres DB | 4455 (✗) | Postgres database for storing user data |
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# The URL of ORY Hydras admin API
|
||||
# The URL of Ory Hydras admin API
|
||||
HYDRA_ADMIN_API=http://hydra:4445
|
||||
|
|
|
@ -70,6 +70,39 @@ services:
|
|||
networks:
|
||||
- internal
|
||||
|
||||
ory-keto-migrate:
|
||||
container_name: ory-keto-migrate
|
||||
image: oryd/keto:v0.12.0
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- ./ory/keto:/etc/config/keto
|
||||
- ory-keto-data:/home/ory
|
||||
- ory-keto-data:/var/lib/sqlite
|
||||
command: migrate -c /etc/config/keto/keto.yaml up --yes
|
||||
depends_on:
|
||||
ory-postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- internal
|
||||
|
||||
|
||||
ory-keto:
|
||||
container_name: ory-keto
|
||||
image: oryd/keto:v0.12.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 127.0.0.1:4466:4466 # public
|
||||
- 127.0.0.1:4467:4467 # admin
|
||||
volumes:
|
||||
- ./ory/keto:/etc/config/keto
|
||||
- ory-keto-data:/home/ory
|
||||
- ory-keto-data:/var/lib/sqlite
|
||||
command: serve -c /etc/config/keto/keto.yaml all
|
||||
depends_on:
|
||||
ory-keto-migrate:
|
||||
condition: service_completed_successfully
|
||||
networks:
|
||||
- internal
|
||||
|
||||
ory-mailslurper:
|
||||
container_name: ory-mailslurper
|
||||
|
@ -111,4 +144,5 @@ networks:
|
|||
volumes:
|
||||
ory-kratos-data:
|
||||
ory-hydra-data:
|
||||
ory-keto-data:
|
||||
ory-postgres-data:
|
||||
|
|
24
docker/ory-dev/keto-make-admin.sh
Normal file
24
docker/ory-dev/keto-make-admin.sh
Normal file
|
@ -0,0 +1,24 @@
|
|||
# this script gives the referenced identity the admin role
|
||||
# make sure to provide the id of the identity
|
||||
|
||||
# check if a identity id argument was provided
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: please provide an identity id."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# set user id variable
|
||||
IDENTITY_ID=$1
|
||||
|
||||
# execute curl to Ory Keto write endpoint
|
||||
curl --request PUT \
|
||||
--url http://localhost:4467/admin/relation-tuples \
|
||||
--data '{
|
||||
"namespace": "roles",
|
||||
"object": "admin",
|
||||
"relation": "member",
|
||||
"subject_id": "'"$IDENTITY_ID"'"
|
||||
}'
|
||||
|
||||
# write success response to terminal
|
||||
echo "Applied admin role to the user with ID $IDENTITY_ID"
|
43
docker/ory-dev/ory/keto/keto.yaml
Normal file
43
docker/ory-dev/ory/keto/keto.yaml
Normal file
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# Documentation: https://www.ory.sh/docs/keto/reference/configuration
|
||||
# Configuration UI: https://www.ory.sh/docs/keto/reference/configuration-editor
|
||||
#
|
||||
|
||||
#
|
||||
# Configure the Keto logging
|
||||
#
|
||||
log:
|
||||
level: info
|
||||
format: text
|
||||
leak_sensitive_values: true
|
||||
|
||||
#
|
||||
# Configure the datasource. Alternative for development purposes is 'memory' (not persistent!)
|
||||
#
|
||||
dsn: postgres://postgres:postgres@ory-postgres:5432/keto?sslmode=disable&max_conns=20&max_idle_conns=4
|
||||
|
||||
#
|
||||
# Set the required namespaces
|
||||
#
|
||||
namespaces:
|
||||
- id: 0
|
||||
name: roles
|
||||
|
||||
serve:
|
||||
read:
|
||||
host: 0.0.0.0
|
||||
port: 4466
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- http://localhost:3000
|
||||
- http://localhost:4000
|
||||
|
||||
write:
|
||||
host: 0.0.0.0
|
||||
port: 4467
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_origins:
|
||||
- http://localhost:3000
|
||||
- http://localhost:4000
|
|
@ -15,9 +15,6 @@
|
|||
"credentials": {
|
||||
"password": {
|
||||
"identifier": true
|
||||
},
|
||||
"webauthn": {
|
||||
"identifier": true
|
||||
}
|
||||
},
|
||||
"recovery": {
|
||||
|
|
|
@ -3,3 +3,6 @@ GRANT ALL PRIVILEGES ON DATABASE kratos TO postgres;
|
|||
|
||||
CREATE DATABASE hydra;
|
||||
GRANT ALL PRIVILEGES ON DATABASE hydra TO postgres;
|
||||
|
||||
CREATE DATABASE keto;
|
||||
GRANT ALL PRIVILEGES ON DATABASE keto TO postgres;
|
||||
|
|
Loading…
Add table
Reference in a new issue