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
11 changes: 11 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ AWS_REGION=
AWS_S3_BUCKET_NAME=

# optional
#LDAP_ENABLED=true
#LDAP_TLS_NO_VERIFY=false
#LDAP_URL='ldaps://ldap.example.com:636/'
#LDAP_BIND_USER='CN=searchuser,OU=systemaccounts,DC=example,DC=com'
#LDAP_BIND_PASSWORD='searchuserpassword'
#LDAP_SEARCH_DN='OU=users,DC=example,DC=com'
#LDAP_USERS_SEARCH_FILTER='(&(objectClass=Person)(objectCategory=Person)(|(mail={{email}})(sAMAccountName={{email}})))'
#LDAP_ATTRIBUTE_LAST_NAME='sn'
#LDAP_ATTRIBUTE_FIRST_NAME='givenName'
#LDAP_ATTRIBUTE_MAIL='mail'

#HTTPS_KEY_PATH='./secrets/ssl.key'
#HTTPS_CERT_PATH='./secrets/ssl.cert'
#SERVER_TIMEOUT=120000
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,28 @@ jobs:
- name: Run e2e tests
run: npm run test:e2e

- name: Dump docker logs on failure
if: failure()
uses: jwalton/gh-docker-logs@v2

- name: Stop containers
if: always()
run: docker compose down

- name: Run ldap containers
run: docker compose -f docker-compose.yml -f docker-compose.ldap.yml up -d

- name: Run ldap test
run: npm run test:ldap

- name: Dump docker logs on failure
if: failure()
uses: jwalton/gh-docker-logs@v2

- name: Stop ldap containers
if: always()
run: docker compose -f docker-compose.yml -f docker-compose.ldap.yml down

- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@
## Local HTTPS config

- Generate keys [here](https://www.selfsignedcertificate.com/)
- place in folder `/secrets` named `ssl.cert` and `ssl.key`
- place in folder `/secrets` named `ssl.cert` and `ssl.key`

## Local LDAP test server

- Run `docker compose -f docker-compose.yml -f docker-compose.ldap.yml ` (see [docker docs for multiple-compose-files - merge](https://docs.docker.com/compose/multiple-compose-files/merge/))
- test the login with ldap and have a look at the logs
```sh
curl 'http://localhost:4200/users/login' \
-H 'accept: */*' \
-H 'accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7,fr;q=0.6' \
-H 'content-type: application/json' \
--data-raw '{"email":"[email protected]","password":"password"}'
```
27 changes: 27 additions & 0 deletions docker-compose.ldap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
services:
api:
environment:
LDAP_ENABLED: 'true'
LDAP_TLS_NO_VERIFY: 'true'
LDAP_URL: 'ldaps://ldapmock:636/'
LDAP_BIND_USER: 'cn=admin,dc=ldapmock,dc=local'
LDAP_BIND_PASSWORD: 'adminpass'
LDAP_SEARCH_DN: 'ou=people,dc=ldapmock,dc=local'
LDAP_USERS_SEARCH_FILTER: '(&(objectClass=person)(mail={{email}}))'
LDAP_ATTRIBUTE_LAST_NAME: 'sn'
LDAP_ATTRIBUTE_FIRST_NAME: 'givenName'
LDAP_ATTRIBUTE_MAIL: 'mail'
depends_on:
postgres:
condition: service_healthy
ldapmock:
condition: service_started
ldapmock:
container_name: ldapmock
# See https://github.com/docker-ThoTeam/slapd-server-mock/tree/main
# Default users in LDAP: https://github.com/docker-ThoTeam/slapd-server-mock/blob/main/bootstrap/data.ldif.TEMPLATE
# e.g.: [email protected]:password
image: thoteam/slapd-server-mock:latest
restart: always
ports:
- "636:636"
101 changes: 95 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"test:cov": "jest --projects src --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --projects src --runInBand",
"test:e2e": "jest --projects test",
"test:acceptance": "jest --projects test_acceptance"
"test:acceptance": "jest --projects test_acceptance",
"test:ldap": "jest --projects test_ldap"
},
"engines": {
"node": ">=18.12.0"
Expand All @@ -47,6 +48,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"fs-extra": "^11.1.1",
"ldapts": "^7.1.0",
"looks-same": "^9.0.0",
"odiff-bin": "^2.6.1",
"passport": "^0.6.0",
Expand Down
75 changes: 75 additions & 0 deletions src/users/db/dbusers.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
import { CreateUserDto } from '../dto/user-create.dto';
import { UserLoginResponseDto } from '../dto/user-login-response.dto';
import { PrismaService } from '../../prisma/prisma.service';
import { User } from '@prisma/client';
import { UpdateUserDto } from '../dto/user-update.dto';
import { AuthService } from '../../auth/auth.service';
import { UserLoginRequestDto } from '../dto/user-login-request.dto';
import { Users } from '../users.interface';

export class DbUsersService implements Users {
private readonly logger: Logger = new Logger(DbUsersService.name);

constructor(
private readonly prismaService: PrismaService,
private readonly authService: AuthService
) {}

async create(createUserDto: CreateUserDto): Promise<UserLoginResponseDto> {
const user = {
email: createUserDto.email.trim().toLowerCase(),
firstName: createUserDto.firstName,
lastName: createUserDto.lastName,
apiKey: this.authService.generateApiKey(),
password: await this.authService.encryptPassword(createUserDto.password),
};

const userData = await this.prismaService.user.create({
data: user,
});

return new UserLoginResponseDto(userData, null);
}

async update(id: string, userDto: UpdateUserDto): Promise<UserLoginResponseDto> {
const user = await this.prismaService.user.update({
where: { id },
data: {
email: userDto.email,
firstName: userDto.firstName,
lastName: userDto.lastName,
},
});
const token = this.authService.signToken(user);
return new UserLoginResponseDto(user, token);
}

async changePassword(user: User, newPassword: string): Promise<boolean> {
await this.prismaService.user.update({
where: { id: user.id },
data: {
password: await this.authService.encryptPassword(newPassword),
},
});
return true;
}

async login(userLoginRequestDto: UserLoginRequestDto) {
const user = await this.prismaService.user.findUnique({
where: { email: userLoginRequestDto.email },
});
if (!user) {
throw new HttpException('Invalid email or password.', HttpStatus.BAD_REQUEST);
}

const isMatch = await this.authService.compare(userLoginRequestDto.password, user.password);

if (!isMatch) {
throw new HttpException('Invalid email or password.', HttpStatus.BAD_REQUEST);
}

const token = this.authService.signToken(user);
return new UserLoginResponseDto(user, token);
}
}
Loading
Loading