Compare commits

...

26 Commits

Author SHA1 Message Date
Marko Barišić
20a8100081 Add jobs for pushing ARM packages (#1765)
* Add jobs for pushing ARM packages
2024-02-27 12:10:14 +01:00
Deda
dd65bf6abb Override version to 2.15.0-rc5 2024-02-26 23:03:37 +01:00
Marko Barišić
28e1618905 Minor update to the rc workflow (#1760)
* Increase ARM build timeout to 120 minutes

* Remove PushToS3 job and make each Package job push to S3 individually

* Expand ARM timeout to 150 minutes for added safety; revert this after release
2024-02-26 23:02:40 +01:00
andrejtonev
2a54c69fc4 Introduce multi-tenancy to SHOW REPLICAS (#1735)
---------

Co-authored-by: Gareth Lloyd <gareth.lloyd@memgraph.io>
2024-02-26 23:02:40 +01:00
andrejtonev
6132fef7b5 Correctly call driver v1 tests (#1630) 2024-02-26 23:02:40 +01:00
andrejtonev
2c322c0f87 GetInfo simplification (#1621)
* Removed force dir in the GetInfo functions
2024-02-26 23:02:40 +01:00
Deda
51aaccbab0 Override version to 2.15.0-rc4 2024-02-26 00:10:11 +01:00
andrejtonev
1dfeee14f7 Better auth user/role handling (#1699)
* Stop auth module from creating users
* Explicit about auth policy (check if no users defined OR auth module used)
* Role supports database access definition
* Authenticate() returns user or role
* AuthChecker generates QueryUserOrRole (can be empty)
* QueryUserOrRole actually authorizes
* Add auth cache invalidation
* Better database access queries (GRANT, DENY, REVOKE DATABASE)
2024-02-26 00:09:31 +01:00
Marko Budiselić
f469428b8e Update operating systems (#1371) 2024-02-26 00:09:31 +01:00
Aidar Samerkhanov
ba34688153 In BFS expansion filter by path we should shrink path to restore state prior to expansion only if the path was changed. (#1745) 2024-02-26 00:09:31 +01:00
Aidar Samerkhanov
6766b5bc96 Revert "In BFS expansion filter by path we should shrink path to restore state prior to expansion only if the path was changed. (#1745)"
This reverts commit 7cf7bebaa6.
2024-02-23 08:45:39 +04:00
Aidar Samerkhanov
7cf7bebaa6 In BFS expansion filter by path we should shrink path to restore state prior to expansion only if the path was changed. (#1745) 2024-02-22 23:08:11 +04:00
Deda
f3d3275aaa Override version to 2.15.0-rc3 2024-02-22 11:58:11 +01:00
Marko Barišić
312cdaf685 Push successful RC builds to S3 (#1741)
* Add new workflow which calls release build workflows

* Make the workflow build packages only on RC tags

* Change artifact names to include OS name
2024-02-21 17:13:36 +01:00
Marko Budiselić
7f8a4f2a8b Add toolchain-v5 compatibility Revert to C++20 (#587)
* Upgrade cppitertools, spdlog, fmt, rapidcheck
* Make compilation work on both v4 and v5 toolchains
2024-02-21 17:13:36 +01:00
Andi
381feb7b35 Add --experimental-enabled=high-availability (#1720) 2024-02-21 17:13:36 +01:00
Marko Budiselić
710dba6a00 Patch NuRaft for clang-17 compilation (#1733) 2024-02-21 17:13:36 +01:00
Josipmrden
db79d7f55e Add function for property sizes (#1557)
Add function for property sizes
2024-02-21 17:11:57 +01:00
Deda
d1cdf9a0ba Override memgraph version to 2.15.0 2024-02-16 17:33:09 +01:00
Gareth Andrew Lloyd
be8b755673 Fixup memory e2e tests (#1715)
- Remove the e2e that did concurrent mgp_* calls on the same transaction
  (ATM this is unsupported)
- Fix up the concurrent mgp_global_alloc test to be testing it more precisely
- Reduce the memory limit on detach delete test due to recent memory
  optimizations around deltas.
- No longer throw from hook, through jemalloc C, to our C++ on other
  side. This cause mutex unlocks to not happen.
- No longer allocate error messages while inside the hook. This caused
  recursive entry back inside jamalloc which would try to relock a
  non-recursive mutex.
2024-02-16 17:31:59 +01:00
Andi
48db39a16a Forbid having multiple mains in the cluster (#1727) 2024-02-16 17:31:22 +01:00
Antonio Filipovic
0f7d774fc5 HA: Polish flow for replicas from coordinator (#1711) 2024-02-16 17:31:09 +01:00
Andi
dfb3a62cc4 Forbid writing to cluster-managed main on restart (#1717) 2024-02-16 17:30:53 +01:00
Marko Barišić
307c2e0881 Turn e2e tests back on for release build workflows (#1725) 2024-02-15 16:22:36 +01:00
Deda
b9b221cfe2 Update BSL license date 2024-02-15 15:50:41 +01:00
Marko Barišić
6698d48a82 Add rules for rc workflows (#1722) 2024-02-15 15:48:50 +01:00
241 changed files with 7404 additions and 2768 deletions

View File

@@ -336,53 +336,6 @@ jobs:
# multiple paths could be defined # multiple paths could be defined
build/logs build/logs
experimental_build_ha:
name: "High availability build"
runs-on: [self-hosted, Linux, X64, Diff]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
source /opt/toolchain-v4/activate
./init
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DMG_EXPERIMENTAL_HIGH_AVAILABILITY=ON ..
make -j$THREADS
- name: Run unit tests
run: |
source /opt/toolchain-v4/activate
cd build
ctest -R memgraph__unit --output-on-failure -j$THREADS
- name: Run e2e tests
if: false
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
source ve3/bin/activate_e2e
cd e2e
./run.sh "Coordinator"
./run.sh "Client initiated failover"
./run.sh "Uninitialized cluster"
- name: Save test data
uses: actions/upload-artifact@v4
if: always()
with:
name: "Test data(High availability build)"
path: |
# multiple paths could be defined
build/logs
release_jepsen_test: release_jepsen_test:
name: "Release Jepsen Test" name: "Release Jepsen Test"
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl] runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]

View File

@@ -0,0 +1,208 @@
name: Release build test
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
build_type:
type: choice
description: "Memgraph Build type. Default value is Release."
default: 'Release'
options:
- Release
- RelWithDebInfo
push:
branches:
- "release/**"
tags:
- "v*.*.*-rc*"
- "v*.*-rc*"
schedule:
# UTC
- cron: "0 22 * * *"
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
BUILD_TYPE: ${{ github.event.inputs.build_type || 'Release' }}
jobs:
Debian10:
uses: ./.github/workflows/release_debian10.yaml
with:
build_type: ${{ github.event.inputs.build_type || 'Release' }}
secrets: inherit
Ubuntu20_04:
uses: ./.github/workflows/release_ubuntu2004.yaml
with:
build_type: ${{ github.event.inputs.build_type || 'Release' }}
secrets: inherit
PackageDebian10:
if: github.ref_type == 'tag'
needs: [Debian10]
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-10 $BUILD_TYPE
- name: Upload to S3
uses: jakejarvis/s3-sync-action@v0.5.1
env:
AWS_S3_BUCKET: "deps.memgraph.io"
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output"
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
- name: "Upload package"
uses: actions/upload-artifact@v4
with:
name: debian-10
path: build/output/debian-10/memgraph*.deb
PackageUbuntu20_04:
if: github.ref_type == 'tag'
needs: [Ubuntu20_04]
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package ubuntu-22.04 $BUILD_TYPE
- name: Upload to S3
uses: jakejarvis/s3-sync-action@v0.5.1
env:
AWS_S3_BUCKET: "deps.memgraph.io"
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output"
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
- name: "Upload package"
uses: actions/upload-artifact@v4
with:
name: ubuntu-22.04
path: build/output/ubuntu-22.04/memgraph*.deb
PackageUbuntu20_04_ARM:
if: github.ref_type == 'tag'
needs: [Ubuntu20_04]
runs-on: [self-hosted, DockerMgBuild, ARM64]
# M1 Mac mini is sometimes slower
timeout-minutes: 150
steps:
- name: "Set up repository"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package ubuntu-22.04-arm $BUILD_TYPE
- name: "Upload package"
uses: actions/upload-artifact@v4
with:
name: ubuntu-22.04-aarch64
path: build/output/ubuntu-22.04-arm/memgraph*.deb
PushToS3Ubuntu20_04_ARM:
if: github.ref_type == 'tag'
needs: [PackageUbuntu20_04_ARM]
runs-on: ubuntu-latest
steps:
- name: Download package
uses: actions/download-artifact@v4
with:
name: ubuntu-22.04-aarch64
path: build/output/release
- name: Upload to S3
uses: jakejarvis/s3-sync-action@v0.5.1
env:
AWS_S3_BUCKET: "deps.memgraph.io"
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output/release"
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
PackageDebian11:
if: github.ref_type == 'tag'
needs: [Debian10, Ubuntu20_04]
runs-on: [self-hosted, DockerMgBuild, X64]
timeout-minutes: 60
steps:
- name: "Set up repository"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-11 $BUILD_TYPE
- name: Upload to S3
uses: jakejarvis/s3-sync-action@v0.5.1
env:
AWS_S3_BUCKET: "deps.memgraph.io"
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output"
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
- name: "Upload package"
uses: actions/upload-artifact@v4
with:
name: debian-11
path: build/output/debian-11/memgraph*.deb
PackageDebian11_ARM:
if: github.ref_type == 'tag'
needs: [Debian10, Ubuntu20_04]
runs-on: [self-hosted, DockerMgBuild, ARM64]
# M1 Mac mini is sometimes slower
timeout-minutes: 150
steps:
- name: "Set up repository"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required because of release/get_version.py
- name: "Build package"
run: |
./release/package/run.sh package debian-11-arm $BUILD_TYPE
- name: "Upload package"
uses: actions/upload-artifact@v4
with:
name: debian-11-aarch64
path: build/output/debian-11-arm/memgraph*.deb
PushToS3Debian11_ARM:
if: github.ref_type == 'tag'
needs: [PackageDebian11_ARM]
runs-on: ubuntu-latest
steps:
- name: Download package
uses: actions/download-artifact@v4
with:
name: debian-11-aarch64
path: build/output/release
- name: Upload to S3
uses: jakejarvis/s3-sync-action@v0.5.1
env:
AWS_S3_BUCKET: "deps.memgraph.io"
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "eu-west-1"
SOURCE_DIR: "build/output/release"
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"

View File

@@ -1,6 +1,12 @@
name: Release Debian 10 name: Release Debian 10
on: on:
workflow_call:
inputs:
build_type:
type: string
description: "Memgraph Build type. Default value is Release."
default: 'Release'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
build_type: build_type:
@@ -11,10 +17,8 @@ on:
- Release - Release
- RelWithDebInfo - RelWithDebInfo
schedule:
- cron: "0 22 * * *"
env: env:
OS: "Debian10"
THREADS: 24 THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }} MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }} MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
@@ -111,7 +115,7 @@ jobs:
- name: Save code coverage - name: Save code coverage
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "Code coverage(Coverage build)" name: "Code coverage(Coverage build)-${{ env.OS }}"
path: tools/github/generated/code_coverage.tar.gz path: tools/github/generated/code_coverage.tar.gz
debug_build: debug_build:
@@ -165,7 +169,7 @@ jobs:
- name: Save cppcheck and clang-format errors - name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "Code coverage(Debug build)" name: "Code coverage(Debug build)-${{ env.OS }}"
path: tools/github/cppcheck_and_clang_format.txt path: tools/github/cppcheck_and_clang_format.txt
debug_integration_test: debug_integration_test:
@@ -242,7 +246,7 @@ jobs:
- name: Save enterprise DEB package - name: Save enterprise DEB package
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "Enterprise DEB package" name: "Enterprise DEB package-${{ env.OS}}"
path: build/output/memgraph*.deb path: build/output/memgraph*.deb
- name: Run GQL Behave tests - name: Run GQL Behave tests
@@ -255,7 +259,7 @@ jobs:
- name: Save quality assurance status - name: Save quality assurance status
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "GQL Behave Status" name: "GQL Behave Status-${{ env.OS }}"
path: | path: |
tests/gql_behave/gql_behave_status.csv tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html tests/gql_behave/gql_behave_status.html
@@ -321,7 +325,6 @@ jobs:
--no-strict --no-strict
release_e2e_test: release_e2e_test:
if: false
name: "Release End-to-end Test" name: "Release End-to-end Test"
runs-on: [self-hosted, Linux, X64, Debian10] runs-on: [self-hosted, Linux, X64, Debian10]
timeout-minutes: 60 timeout-minutes: 60
@@ -456,5 +459,5 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: ${{ always() }} if: ${{ always() }}
with: with:
name: "Jepsen Report" name: "Jepsen Report-${{ env.OS }}"
path: tests/jepsen/Jepsen.tar.gz path: tests/jepsen/Jepsen.tar.gz

View File

@@ -1,6 +1,12 @@
name: Release Ubuntu 20.04 name: Release Ubuntu 20.04
on: on:
workflow_call:
inputs:
build_type:
type: string
description: "Memgraph Build type. Default value is Release."
default: 'Release'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
build_type: build_type:
@@ -11,10 +17,8 @@ on:
- Release - Release
- RelWithDebInfo - RelWithDebInfo
schedule:
- cron: "0 22 * * *"
env: env:
OS: "Ubuntu 20.04"
THREADS: 24 THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }} MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }} MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
@@ -107,7 +111,7 @@ jobs:
- name: Save code coverage - name: Save code coverage
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "Code coverage(Coverage build)" name: "Code coverage(Coverage build)-${{ env.OS }}"
path: tools/github/generated/code_coverage.tar.gz path: tools/github/generated/code_coverage.tar.gz
debug_build: debug_build:
@@ -161,7 +165,7 @@ jobs:
- name: Save cppcheck and clang-format errors - name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "Code coverage(Debug build)" name: "Code coverage(Debug build)-${{ env.OS }}"
path: tools/github/cppcheck_and_clang_format.txt path: tools/github/cppcheck_and_clang_format.txt
debug_integration_test: debug_integration_test:
@@ -238,7 +242,7 @@ jobs:
- name: Save enterprise DEB package - name: Save enterprise DEB package
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "Enterprise DEB package" name: "Enterprise DEB package-${{ env.OS }}"
path: build/output/memgraph*.deb path: build/output/memgraph*.deb
- name: Run GQL Behave tests - name: Run GQL Behave tests
@@ -251,7 +255,7 @@ jobs:
- name: Save quality assurance status - name: Save quality assurance status
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: "GQL Behave Status" name: "GQL Behave Status-${{ env.OS }}"
path: | path: |
tests/gql_behave/gql_behave_status.csv tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html tests/gql_behave/gql_behave_status.html
@@ -317,7 +321,6 @@ jobs:
--no-strict --no-strict
release_e2e_test: release_e2e_test:
if: false
name: "Release End-to-end Test" name: "Release End-to-end Test"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04] runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
timeout-minutes: 60 timeout-minutes: 60

View File

@@ -1,4 +1,7 @@
name: Stress test large name: Stress test large
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
on: on:
workflow_dispatch: workflow_dispatch:
@@ -10,7 +13,10 @@ on:
options: options:
- Release - Release
- RelWithDebInfo - RelWithDebInfo
push:
tags:
- "v*.*.*-rc*"
- "v*.*-rc*"
schedule: schedule:
- cron: "0 22 * * *" - cron: "0 22 * * *"

View File

@@ -64,11 +64,11 @@ option(MG_ENTERPRISE "Build Memgraph Enterprise Edition" ON)
# Set the current version here to override the automatic version detection. The # Set the current version here to override the automatic version detection. The
# version must be specified as `X.Y.Z`. Primarily used when building new patch # version must be specified as `X.Y.Z`. Primarily used when building new patch
# versions. # versions.
set(MEMGRAPH_OVERRIDE_VERSION "") set(MEMGRAPH_OVERRIDE_VERSION "2.15.0")
# Custom suffix that this version should have. The suffix can be any arbitrary # Custom suffix that this version should have. The suffix can be any arbitrary
# string. Primarily used when building a version for a specific customer. # string. Primarily used when building a version for a specific customer.
set(MEMGRAPH_OVERRIDE_VERSION_SUFFIX "") set(MEMGRAPH_OVERRIDE_VERSION_SUFFIX "rc5")
# Variables used to generate the versions. # Variables used to generate the versions.
if (MG_ENTERPRISE) if (MG_ENTERPRISE)
@@ -211,8 +211,13 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
# ** Static linking is allowed only for executables! ** # ** Static linking is allowed only for executables! **
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
# Use lld linker to speedup build # Use lld linker to speedup build and use less memory.
add_link_options(-fuse-ld=lld) # TODO: use mold linker add_link_options(-fuse-ld=lld)
# NOTE: Moving to latest Clang (probably starting from 15), lld stopped to work
# without explicit link_directories call.
string(REPLACE ":" " " LD_LIBS $ENV{LD_LIBRARY_PATH})
separate_arguments(LD_LIBS)
link_directories(${LD_LIBS})
# release flags # release flags
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
@@ -271,18 +276,6 @@ endif()
set(libs_dir ${CMAKE_SOURCE_DIR}/libs) set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
add_subdirectory(libs EXCLUDE_FROM_ALL) add_subdirectory(libs EXCLUDE_FROM_ALL)
option(MG_EXPERIMENTAL_HIGH_AVAILABILITY "Feature flag for experimental high availability" OFF)
if (NOT MG_ENTERPRISE AND MG_EXPERIMENTAL_HIGH_AVAILABILITY)
set(MG_EXPERIMENTAL_HIGH_AVAILABILITY OFF)
message(FATAL_ERROR "MG_EXPERIMENTAL_HIGH_AVAILABILITY can only be used with enterpise version of the code.")
endif ()
if (MG_EXPERIMENTAL_HIGH_AVAILABILITY)
add_compile_definitions(MG_EXPERIMENTAL_HIGH_AVAILABILITY)
endif ()
# Optional subproject configuration -------------------------------------------
option(TEST_COVERAGE "Generate coverage reports from running memgraph" OFF) option(TEST_COVERAGE "Generate coverage reports from running memgraph" OFF)
option(TOOLS "Build tools binaries" ON) option(TOOLS "Build tools binaries" ON)
option(QUERY_MODULES "Build query modules containing custom procedures" ON) option(QUERY_MODULES "Build query modules containing custom procedures" ON)

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -9,7 +7,7 @@ check_operating_system "amzn-2"
check_architecture "x86_64" check_architecture "x86_64"
TOOLCHAIN_BUILD_DEPS=( TOOLCHAIN_BUILD_DEPS=(
gcc gcc-c++ make # generic build tools git gcc gcc-c++ make # generic build tools
wget # used for archive download wget # used for archive download
gnupg2 # used for archive signature verification gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking tar gzip bzip2 xz unzip # used for archive unpacking
@@ -63,6 +61,8 @@ MEMGRAPH_BUILD_DEPS=(
cyrus-sasl-devel cyrus-sasl-devel
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp logrotate openssl python3 libseccomp
) )

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -63,6 +61,8 @@ MEMGRAPH_BUILD_DEPS=(
cyrus-sasl-devel cyrus-sasl-devel
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp logrotate openssl python3 libseccomp
) )

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -9,8 +7,10 @@ check_operating_system "centos-9"
check_architecture "x86_64" check_architecture "x86_64"
TOOLCHAIN_BUILD_DEPS=( TOOLCHAIN_BUILD_DEPS=(
coreutils-common gcc gcc-c++ make # generic build tools
wget # used for archive download wget # used for archive download
coreutils-common gcc gcc-c++ make # generic build tools
# NOTE: Pure libcurl conflicts with libcurl-minimal
libcurl-devel # cmake build requires it
gnupg2 # used for archive signature verification gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking tar gzip bzip2 xz unzip # used for archive unpacking
zlib-devel # zlib library used for all builds zlib-devel # zlib library used for all builds
@@ -64,6 +64,8 @@ MEMGRAPH_BUILD_DEPS=(
cyrus-sasl-devel cyrus-sasl-devel
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp logrotate openssl python3 libseccomp
) )
@@ -123,7 +125,9 @@ install() {
else else
echo "NOTE: export LANG=en_US.utf8" echo "NOTE: export LANG=en_US.utf8"
fi fi
yum update -y # --nobest is used because of libipt because we install custom versions
# because libipt-devel is not available on CentOS 9 Stream
yum update -y --nobest
yum install -y wget git python3 python3-pip yum install -y wget git python3 python3-pip
for pkg in $1; do for pkg in $1; do

View File

@@ -1,10 +1,10 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
# IMPORTANT: Deprecated since memgraph v2.12.0.
check_operating_system "debian-10" check_operating_system "debian-10"
check_architecture "x86_64" check_architecture "x86_64"

View File

@@ -1,10 +1,10 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
# IMPORTANT: Deprecated since memgraph v2.12.0.
check_operating_system "debian-11" check_operating_system "debian-11"
check_architecture "arm64" "aarch64" check_architecture "arm64" "aarch64"

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -61,6 +59,8 @@ MEMGRAPH_BUILD_DEPS=(
libsasl2-dev libsasl2-dev
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp logrotate openssl python3 libseccomp
) )

134
environment/os/debian-12-arm.sh Executable file
View File

@@ -0,0 +1,134 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
check_operating_system "debian-12"
check_architecture "arm64" "aarch64"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre2-dev libpcre3-dev automake bison # for swig
curl # snappy
file # for libunwind
libssl-dev # for libevent
libgmp-dev
gperf # for proxygen
git # for fbthrift
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 liblzma5 python3 # for gdb
libcurl4 # for cmake
file # for CPack
libreadline8 # for cmake and llvm
libffi8 libxml2 # for llvm
libssl-dev # for libevent
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkg-config # build system
curl wget # for downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # for memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
netcat # tests are using nc to wait for memgraph
python3 virtualenv python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
python3-yaml # for the configuration generator
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless custom-maven3.9.3 # for driver tests
dotnet-sdk-7.0 golang custom-golang1.18.9 nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf code generation
libsasl2-dev
)
MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp
)
NEW_DEPS=(
wget curl tar gzip
)
list() {
echo "$1"
}
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then
if [ ! -f "/opt/go1.18.9/go/bin/go" ]; then
missing="$pkg $missing"
fi
continue
fi
if ! dpkg -s "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
install() {
cd "$DIR"
apt update
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
apt install -y wget
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then
install_custom_golang "1.18.9"
continue
fi
if [ "$pkg" == dotnet-sdk-7.0 ]; then
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
wget -nv https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
apt-get update
apt-get install -y apt-transport-https dotnet-sdk-7.0
fi
continue
fi
apt install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

136
environment/os/debian-12.sh Executable file
View File

@@ -0,0 +1,136 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
check_operating_system "debian-12"
check_architecture "x86_64"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre2-dev libpcre3-dev automake bison # for swig
curl # snappy
file # for libunwind
libssl-dev # for libevent
libgmp-dev
gperf # for proxygen
git # for fbthrift
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt2 libbabeltrace1 liblzma5 python3 # for gdb
libcurl4 # for cmake
file # for CPack
libreadline8 # for cmake and llvm
libffi8 libxml2 # for llvm
libssl-dev # for libevent
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make cmake pkg-config # build system
curl wget # for downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # for memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
netcat-traditional # tests are using nc to wait for memgraph
python3 virtualenv python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
python3-yaml # for the configuration generator
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless custom-maven3.9.3 # for driver tests
dotnet-sdk-7.0 golang custom-golang1.18.9 nodejs npm
autoconf # for jemalloc code generation
libtool # for protobuf code generation
libsasl2-dev
)
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp
)
NEW_DEPS=(
wget curl tar gzip
)
list() {
echo "$1"
}
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then
if [ ! -f "/opt/go1.18.9/go/bin/go" ]; then
missing="$pkg $missing"
fi
continue
fi
if ! dpkg -s "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
install() {
cd "$DIR"
apt update
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
apt install -y wget
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then
install_custom_golang "1.18.9"
continue
fi
if [ "$pkg" == dotnet-sdk-7.0 ]; then
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
wget -nv https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
apt-get update
apt-get install -y apt-transport-https dotnet-sdk-7.0
fi
continue
fi
apt install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

View File

@@ -1,10 +1,10 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
# IMPORTANT: Deprecated since memgraph v2.12.0.
check_operating_system "fedora-36" check_operating_system "fedora-36"
check_architecture "x86_64" check_architecture "x86_64"
@@ -27,6 +27,7 @@ TOOLCHAIN_BUILD_DEPS=(
libipt libipt-devel # intel libipt libipt-devel # intel
patch patch
perl # for openssl perl # for openssl
git
) )
TOOLCHAIN_RUN_DEPS=( TOOLCHAIN_RUN_DEPS=(

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -27,6 +25,7 @@ TOOLCHAIN_BUILD_DEPS=(
libipt libipt-devel # intel libipt libipt-devel # intel
patch patch
perl # for openssl perl # for openssl
git
) )
TOOLCHAIN_RUN_DEPS=( TOOLCHAIN_RUN_DEPS=(
@@ -58,6 +57,16 @@ MEMGRAPH_BUILD_DEPS=(
libtool # for protobuf code generation libtool # for protobuf code generation
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp
)
NEW_DEPS=(
wget curl tar gzip
)
list() { list() {
echo "$1" echo "$1"
} }

117
environment/os/fedora-39.sh Executable file
View File

@@ -0,0 +1,117 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
check_operating_system "fedora-39"
check_architecture "x86_64"
TOOLCHAIN_BUILD_DEPS=(
coreutils-common gcc gcc-c++ make # generic build tools
wget # used for archive download
gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking
zlib-devel # zlib library used for all builds
expat-devel xz-devel python3-devel texinfo libbabeltrace-devel # for gdb
curl libcurl-devel # for cmake
readline-devel # for cmake and llvm
libffi-devel libxml2-devel # for llvm
libedit-devel pcre-devel pcre2-devel automake bison # for swig
file
openssl-devel
gmp-devel
gperf
diffutils
libipt libipt-devel # intel
patch
perl # for openssl
git
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz # used for archive unpacking
zlib # zlib library used for all builds
expat xz-libs python3 # for gdb
readline # for cmake and llvm
libffi libxml2 # for llvm
openssl-devel
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkgconf-pkg-config # build system
wget # for downloading libs
libuuid-devel java-11-openjdk # required by antlr
readline-devel # for memgraph console
python3-devel # for query modules
openssl-devel
libseccomp-devel
python3 python3-pip python3-virtualenv python3-virtualenvwrapper python3-pyyaml nmap-ncat # for tests
libcurl-devel # mg-requests
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which nodejs golang zip unzip java-11-openjdk-devel # for driver tests
sbcl # for custom Lisp C++ preprocessing
autoconf # for jemalloc code generation
libtool # for protobuf code generation
)
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp
)
NEW_DEPS=(
wget curl tar gzip
)
list() {
echo "$1"
}
check() {
if [ -v LD_LIBRARY_PATH ]; then
# On Fedora 38 yum/dnf and python11 use newer glibc which is not compatible
# with ours, so we need to momentarely disable env
local OLD_LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
LD_LIBRARY_PATH=""
fi
local missing=""
for pkg in $1; do
if ! dnf list installed "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
if [ -v OLD_LD_LIBRARY_PATH ]; then
echo "Restoring LD_LIBRARY_PATH..."
LD_LIBRARY_PATH=${OLD_LD_LIBRARY_PATH}
fi
}
install() {
cd "$DIR"
if [ "$EUID" -ne 0 ]; then
echo "Please run as root."
exit 1
fi
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests don't work without the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
dnf update -y
for pkg in $1; do
dnf install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

188
environment/os/rocky-9.3.sh Executable file
View File

@@ -0,0 +1,188 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
# TODO(gitbuda): Rocky gets automatically updates -> figure out how to handle it.
check_operating_system "rocky-9.3"
check_architecture "x86_64"
TOOLCHAIN_BUILD_DEPS=(
wget # used for archive download
coreutils-common gcc gcc-c++ make # generic build tools
# NOTE: Pure libcurl conflicts with libcurl-minimal
libcurl-devel # cmake build requires it
gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking
zlib-devel # zlib library used for all builds
expat-devel xz-devel python3-devel perl-Unicode-EastAsianWidth texinfo libbabeltrace-devel # for gdb
readline-devel # for cmake and llvm
libffi-devel libxml2-devel # for llvm
libedit-devel pcre-devel pcre2-devel automake bison # for swig
file
openssl-devel
gmp-devel
gperf
diffutils
libipt libipt-devel # intel
patch
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz # used for archive unpacking
zlib # zlib library used for all builds
expat xz-libs python3 # for gdb
readline # for cmake and llvm
libffi libxml2 # for llvm
openssl-devel
perl # for openssl
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make cmake pkgconf-pkg-config # build system
wget # for downloading libs
libuuid-devel java-11-openjdk # required by antlr
readline-devel # for memgraph console
python3-devel # for query modules
openssl-devel
libseccomp-devel
python3 python3-pip python3-virtualenv nmap-ncat # for qa, macro_benchmark and stress tests
#
# IMPORTANT: python3-yaml does NOT exist on CentOS
# Install it manually using `pip3 install PyYAML`
#
PyYAML # Package name here does not correspond to the yum package!
libcurl-devel # mg-requests
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which nodejs golang custom-golang1.18.9 # for driver tests
zip unzip java-11-openjdk-devel java-17-openjdk java-17-openjdk-devel custom-maven3.9.3 # for driver tests
sbcl # for custom Lisp C++ preprocessing
autoconf # for jemalloc code generation
libtool # for protobuf code generation
cyrus-sasl-devel
)
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp
)
NEW_DEPS=(
wget curl tar gzip
)
list() {
echo "$1"
}
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
missing="$pkg $missing"
fi
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then
if [ ! -f "/opt/go1.18.9/go/bin/go" ]; then
missing="$pkg $missing"
fi
continue
fi
if [ "$pkg" == "PyYAML" ]; then
if ! python3 -c "import yaml" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
continue
fi
if [ "$pkg" == "python3-virtualenv" ]; then
continue
fi
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
install() {
cd "$DIR"
if [ "$EUID" -ne 0 ]; then
echo "Please run as root."
exit 1
fi
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
yum update -y
yum install -y wget git python3 python3-pip
for pkg in $1; do
if [ "$pkg" == custom-maven3.9.3 ]; then
install_custom_maven "3.9.3"
continue
fi
if [ "$pkg" == custom-golang1.18.9 ]; then
install_custom_golang "1.18.9"
continue
fi
if [ "$pkg" == perl-Unicode-EastAsianWidth ]; then
if ! dnf list installed perl-Unicode-EastAsianWidth >/dev/null 2>/dev/null; then
dnf install -y https://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/os/Packages/p/perl-Unicode-EastAsianWidth-12.0-7.el9.noarch.rpm
fi
continue
fi
if [ "$pkg" == texinfo ]; then
if ! dnf list installed texinfo >/dev/null 2>/dev/null; then
dnf install -y https://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/os/Packages/t/texinfo-6.7-15.el9.x86_64.rpm
fi
continue
fi
if [ "$pkg" == libbabeltrace-devel ]; then
if ! dnf list installed libbabeltrace-devel >/dev/null 2>/dev/null; then
dnf install -y https://dl.rockylinux.org/pub/rocky/9/devel/x86_64/os/Packages/l/libbabeltrace-devel-1.5.8-10.el9.x86_64.rpm
fi
continue
fi
if [ "$pkg" == libipt-devel ]; then
if ! dnf list installed libipt-devel >/dev/null 2>/dev/null; then
dnf install -y https://dl.rockylinux.org/pub/rocky/9/devel/x86_64/os/Packages/l/libipt-devel-2.0.4-5.el9.x86_64.rpm
fi
continue
fi
if [ "$pkg" == PyYAML ]; then
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
pip3 install --user PyYAML
else # Running using sudo.
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user PyYAML"
fi
continue
fi
if [ "$pkg" == python3-virtualenv ]; then
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
pip3 install virtualenv
pip3 install virtualenvwrapper
else # Running using sudo.
sudo -H -u "$SUDO_USER" bash -c "pip3 install virtualenv"
sudo -H -u "$SUDO_USER" bash -c "pip3 install virtualenvwrapper"
fi
continue
fi
yum install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -20,6 +18,10 @@ MEMGRAPH_BUILD_DEPS=(
pkg pkg
) )
MEMGRAPH_TEST_DEPS=(
pkg
)
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
pkg pkg
) )

View File

@@ -1,10 +1,10 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
# IMPORTANT: Deprecated since memgraph v2.12.0.
check_operating_system "ubuntu-18.04" check_operating_system "ubuntu-18.04"
check_architecture "x86_64" check_architecture "x86_64"

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -60,6 +58,8 @@ MEMGRAPH_BUILD_DEPS=(
libsasl2-dev libsasl2-dev
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp2 logrotate openssl python3 libseccomp2
) )

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -60,6 +58,8 @@ MEMGRAPH_BUILD_DEPS=(
libsasl2-dev libsasl2-dev
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp2 logrotate openssl python3 libseccomp2
) )

View File

@@ -1,7 +1,5 @@
#!/bin/bash #!/bin/bash
set -Eeuo pipefail set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh" source "$DIR/../util.sh"
@@ -60,6 +58,8 @@ MEMGRAPH_BUILD_DEPS=(
libsasl2-dev libsasl2-dev
) )
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
MEMGRAPH_RUN_DEPS=( MEMGRAPH_RUN_DEPS=(
logrotate openssl python3 libseccomp2 logrotate openssl python3 libseccomp2
) )

View File

@@ -2,3 +2,4 @@ archives
build build
output output
*.tar.gz *.tar.gz
tmp_build.sh

View File

@@ -0,0 +1,48 @@
#!/bin/bash -e
# NOTE: Copy this under memgraph/environment/toolchain/vN/tmp_build.sh, edit and test.
pushd () { command pushd "$@" > /dev/null; }
popd () { command popd "$@" > /dev/null; }
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CPUS=$( grep -c processor < /proc/cpuinfo )
cd "$DIR"
source "$DIR/../../util.sh"
DISTRO="$(operating_system)"
TOOLCHAIN_VERSION=5
NAME=toolchain-v$TOOLCHAIN_VERSION
PREFIX=/opt/$NAME
function log_tool_name () {
echo ""
echo ""
echo "#### $1 ####"
echo ""
echo ""
}
# HERE: Remove/clear dependencies from a given toolchain.
mkdir -p archives && pushd archives
# HERE: Download dependencies here.
popd
mkdir -p build
pushd build
source $PREFIX/activate
export CC=$PREFIX/bin/clang
export CXX=$PREFIX/bin/clang++
export CFLAGS="$CFLAGS -fPIC"
export PATH=$PREFIX/bin:$PATH
export LD_LIBRARY_PATH=$PREFIX/lib64
COMMON_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$PREFIX
-DCMAKE_PREFIX_PATH=$PREFIX
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_C_COMPILER=$CC
-DCMAKE_CXX_COMPILER=$CXX
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_CXX_STANDARD=20
-DBUILD_TESTING=OFF
-DCMAKE_REQUIRED_INCLUDES=$PREFIX/include
-DCMAKE_POSITION_INDEPENDENT_CODE=ON"
# HERE: Add dependencies to test below.

View File

@@ -307,7 +307,7 @@ if [ ! -f $PREFIX/bin/ld.gold ]; then
fi fi
log_tool_name "GDB $GDB_VERSION" log_tool_name "GDB $GDB_VERSION"
if [ ! -f $PREFIX/bin/gdb ]; then if [[ ! -f "$PREFIX/bin/gdb" && "$DISTRO" -ne "amzn-2" ]]; then
if [ -d gdb-$GDB_VERSION ]; then if [ -d gdb-$GDB_VERSION ]; then
rm -rf gdb-$GDB_VERSION rm -rf gdb-$GDB_VERSION
fi fi
@@ -671,7 +671,6 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
# WANGLE_SHA256=0e493c03572bb27fe9ca03a9da5023e52fde99c95abdcaa919bb6190e7e69532 # WANGLE_SHA256=0e493c03572bb27fe9ca03a9da5023e52fde99c95abdcaa919bb6190e7e69532
FLEX_VERSION=2.6.4
FMT_SHA256=78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b FMT_SHA256=78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b
FMT_VERSION=10.1.1 FMT_VERSION=10.1.1
# NOTE: spdlog depends on exact fmt versions -> UPGRADE fmt and spdlog TOGETHER. # NOTE: spdlog depends on exact fmt versions -> UPGRADE fmt and spdlog TOGETHER.
@@ -690,8 +689,8 @@ LZ4_VERSION=1.9.4
SNAPPY_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7 SNAPPY_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7
SNAPPY_VERSION=1.1.9 SNAPPY_VERSION=1.1.9
XZ_VERSION=5.2.5 # for LZMA XZ_VERSION=5.2.5 # for LZMA
ZLIB_VERSION=1.3 ZLIB_VERSION=1.3.1
ZSTD_VERSION=1.5.0 ZSTD_VERSION=1.5.5
pushd archives pushd archives
if [ ! -f boost_$BOOST_VERSION_UNDERSCORES.tar.gz ]; then if [ ! -f boost_$BOOST_VERSION_UNDERSCORES.tar.gz ]; then
@@ -700,7 +699,7 @@ if [ ! -f boost_$BOOST_VERSION_UNDERSCORES.tar.gz ]; then
wget https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$BOOST_VERSION_UNDERSCORES.tar.gz -O boost_$BOOST_VERSION_UNDERSCORES.tar.gz wget https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$BOOST_VERSION_UNDERSCORES.tar.gz -O boost_$BOOST_VERSION_UNDERSCORES.tar.gz
fi fi
if [ ! -f bzip2-$BZIP2_VERSION.tar.gz ]; then if [ ! -f bzip2-$BZIP2_VERSION.tar.gz ]; then
wget https://sourceforge.net/projects/bzip2/files/bzip2-$BZIP2_VERSION.tar.gz -O bzip2-$BZIP2_VERSION.tar.gz wget https://sourceware.org/pub/bzip2/bzip2-$BZIP2_VERSION.tar.gz -O bzip2-$BZIP2_VERSION.tar.gz
fi fi
if [ ! -f double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz ]; then if [ ! -f double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz ]; then
wget https://github.com/google/double-conversion/archive/refs/tags/v$DOUBLE_CONVERSION_VERSION.tar.gz -O double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz wget https://github.com/google/double-conversion/archive/refs/tags/v$DOUBLE_CONVERSION_VERSION.tar.gz -O double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz
@@ -708,9 +707,7 @@ fi
if [ ! -f fizz-$FBLIBS_VERSION.tar.gz ]; then if [ ! -f fizz-$FBLIBS_VERSION.tar.gz ]; then
wget https://github.com/facebookincubator/fizz/releases/download/v$FBLIBS_VERSION/fizz-v$FBLIBS_VERSION.tar.gz -O fizz-$FBLIBS_VERSION.tar.gz wget https://github.com/facebookincubator/fizz/releases/download/v$FBLIBS_VERSION/fizz-v$FBLIBS_VERSION.tar.gz -O fizz-$FBLIBS_VERSION.tar.gz
fi fi
if [ ! -f flex-$FLEX_VERSION.tar.gz ]; then
wget https://github.com/westes/flex/releases/download/v$FLEX_VERSION/flex-$FLEX_VERSION.tar.gz -O flex-$FLEX_VERSION.tar.gz
fi
if [ ! -f fmt-$FMT_VERSION.tar.gz ]; then if [ ! -f fmt-$FMT_VERSION.tar.gz ]; then
wget https://github.com/fmtlib/fmt/archive/refs/tags/$FMT_VERSION.tar.gz -O fmt-$FMT_VERSION.tar.gz wget https://github.com/fmtlib/fmt/archive/refs/tags/$FMT_VERSION.tar.gz -O fmt-$FMT_VERSION.tar.gz
fi fi
@@ -765,14 +762,6 @@ echo "$BZIP2_SHA256 bzip2-$BZIP2_VERSION.tar.gz" | sha256sum -c
echo "$DOUBLE_CONVERSION_SHA256 double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz" | sha256sum -c echo "$DOUBLE_CONVERSION_SHA256 double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz" | sha256sum -c
# verify fizz # verify fizz
echo "$FIZZ_SHA256 fizz-$FBLIBS_VERSION.tar.gz" | sha256sum -c echo "$FIZZ_SHA256 fizz-$FBLIBS_VERSION.tar.gz" | sha256sum -c
# verify flex
if [ ! -f flex-$FLEX_VERSION.tar.gz.sig ]; then
wget https://github.com/westes/flex/releases/download/v$FLEX_VERSION/flex-$FLEX_VERSION.tar.gz.sig
fi
if false; then
$GPG --keyserver $KEYSERVER --recv-keys 0xE4B29C8D64885307
$GPG --verify flex-$FLEX_VERSION.tar.gz.sig flex-$FLEX_VERSION.tar.gz
fi
# verify fmt # verify fmt
echo "$FMT_SHA256 fmt-$FMT_VERSION.tar.gz" | sha256sum -c echo "$FMT_SHA256 fmt-$FMT_VERSION.tar.gz" | sha256sum -c
# verify spdlog # verify spdlog
@@ -1025,7 +1014,6 @@ if [ ! -d $PREFIX/include/gflags ]; then
if [ -d gflags ]; then if [ -d gflags ]; then
rm -rf gflags rm -rf gflags
fi fi
git clone https://github.com/memgraph/gflags.git gflags git clone https://github.com/memgraph/gflags.git gflags
pushd gflags pushd gflags
git checkout $GFLAGS_COMMIT_HASH git checkout $GFLAGS_COMMIT_HASH
@@ -1034,7 +1022,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
cmake .. $COMMON_CMAKE_FLAGS \ cmake .. $COMMON_CMAKE_FLAGS \
-DREGISTER_INSTALL_PREFIX=OFF \ -DREGISTER_INSTALL_PREFIX=OFF \
-DBUILD_gflags_nothreads_LIB=OFF \ -DBUILD_gflags_nothreads_LIB=OFF \
-DGFLAGS_NO_FILENAMES=0 -DGFLAGS_NO_FILENAMES=1
make -j$CPUS install make -j$CPUS install
popd && popd popd && popd
fi fi
@@ -1232,18 +1220,6 @@ if false; then
fi fi
fi fi
log_tool_name "flex $FLEX_VERSION"
if [ ! -f $PREFIX/include/FlexLexer.h ]; then
if [ -d flex-$FLEX_VERSION ]; then
rm -rf flex-$FLEX_VERSION
fi
tar -xzf ../archives/flex-$FLEX_VERSION.tar.gz
pushd flex-$FLEX_VERSION
./configure $COMMON_CONFIGURE_FLAGS
make -j$CPUS install
popd
fi
popd popd
# NOTE: It's important/clean (e.g., easier upload to S3) to have a separated # NOTE: It's important/clean (e.g., easier upload to S3) to have a separated
# folder to the output archive. # folder to the output archive.

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -283,7 +283,7 @@ inline mgp_list *list_all_unique_constraints(mgp_graph *graph, mgp_memory *memor
} }
// mgp_graph // mgp_graph
inline bool graph_is_transactional(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_transactional, graph); } inline bool graph_is_transactional(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_transactional, graph); }
inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); } inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }

1
libs/.gitignore vendored
View File

@@ -7,3 +7,4 @@
!pulsar.patch !pulsar.patch
!antlr4.10.1.patch !antlr4.10.1.patch
!rocksdb8.1.1.patch !rocksdb8.1.1.patch
!nuraft2.1.0.patch

View File

@@ -16,7 +16,7 @@ set(GFLAGS_NOTHREADS OFF)
# NOTE: config/generate.py depends on the gflags help XML format. # NOTE: config/generate.py depends on the gflags help XML format.
find_package(gflags REQUIRED) find_package(gflags REQUIRED)
find_package(fmt 8.0.1) find_package(fmt 8.0.1 REQUIRED)
find_package(ZLIB 1.2.11 REQUIRED) find_package(ZLIB 1.2.11 REQUIRED)
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -5,7 +5,7 @@ index ee9b58c..31359a9 100644
@@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF) @@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF)
# | Library Build and Install Properties | # | Library Build and Install Properties |
# +--------------------------------------------------------+ # +--------------------------------------------------------+
-add_library(rdtsc SHARED -add_library(rdtsc SHARED
+add_library(rdtsc +add_library(rdtsc
src/cycles.c src/cycles.c
@@ -14,7 +14,7 @@ index ee9b58c..31359a9 100644
@@ -72,15 +72,6 @@ target_include_directories(rdtsc @@ -72,15 +72,6 @@ target_include_directories(rdtsc
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
) )
-# Install directory changes depending on build mode -# Install directory changes depending on build mode
-if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug") -if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
- # During debug, the library will be installed into a local directory - # During debug, the library will be installed into a local directory
@@ -27,3 +27,15 @@ index ee9b58c..31359a9 100644
# Specifying what to export when installing (GNUInstallDirs required) # Specifying what to export when installing (GNUInstallDirs required)
install(TARGETS rdtsc install(TARGETS rdtsc
EXPORT librstsc-config EXPORT librstsc-config
diff --git a/include/librdtsc/common_timer.h b/include/librdtsc/common_timer.h
index a6922d8..080dc77 100644
--- a/include/librdtsc/common_timer.h
+++ b/include/librdtsc/common_timer.h
@@ -2,6 +2,7 @@
#define LIBRDTSC_COMMON_TIMER_H
#include <librdtsc/common.h>
+#include <librdtsc/cycles.h>
extern uint64_t rdtsc_get_tsc_freq_arch();
extern uint64_t rdtsc_get_tsc_freq();

24
libs/nuraft2.1.0.patch Normal file
View File

@@ -0,0 +1,24 @@
diff --git a/include/libnuraft/asio_service_options.hxx b/include/libnuraft/asio_service_options.hxx
index 8fe1ec9..9497355 100644
--- a/include/libnuraft/asio_service_options.hxx
+++ b/include/libnuraft/asio_service_options.hxx
@@ -17,6 +17,7 @@ limitations under the License.
#pragma once
+#include <cstdint>
#include <functional>
#include <string>
#include <system_error>
diff --git a/include/libnuraft/callback.hxx b/include/libnuraft/callback.hxx
index 7b71624..d48c1e2 100644
--- a/include/libnuraft/callback.hxx
+++ b/include/libnuraft/callback.hxx
@@ -18,6 +18,7 @@ limitations under the License.
#ifndef _CALLBACK_H_
#define _CALLBACK_H_
+#include <cstdint>
#include <functional>
#include <string>

View File

@@ -1,21 +0,0 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6761929..6a369af 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -220,6 +220,7 @@ else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -momit-leaf-frame-pointer")
endif()
endif()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-copy -Wno-unused-but-set-variable")
endif()
include(CheckCCompilerFlag)
@@ -997,7 +998,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
if(ROCKSDB_BUILD_SHARED)
install(
- TARGETS ${ROCKSDB_SHARED_LIB}
+ TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL
EXPORT RocksDBTargets
COMPONENT runtime
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"

View File

@@ -168,12 +168,11 @@ pushd antlr4
git apply ../antlr4.10.1.patch git apply ../antlr4.10.1.patch
popd popd
# cppitertools v2.0 2019-12-23 cppitertools_ref="v2.1" # 2021-01-15
cppitertools_ref="cb3635456bdb531121b82b4d2e3afc7ae1f56d47"
repo_clone_try_double "${primary_urls[cppitertools]}" "${secondary_urls[cppitertools]}" "cppitertools" "$cppitertools_ref" repo_clone_try_double "${primary_urls[cppitertools]}" "${secondary_urls[cppitertools]}" "cppitertools" "$cppitertools_ref"
# rapidcheck # rapidcheck
rapidcheck_tag="7bc7d302191a4f3d0bf005692677126136e02f60" # (2020-05-04) rapidcheck_tag="1c91f40e64d87869250cfb610376c629307bf77d" # (2023-08-15)
repo_clone_try_double "${primary_urls[rapidcheck]}" "${secondary_urls[rapidcheck]}" "rapidcheck" "$rapidcheck_tag" repo_clone_try_double "${primary_urls[rapidcheck]}" "${secondary_urls[rapidcheck]}" "rapidcheck" "$rapidcheck_tag"
# google benchmark # google benchmark
@@ -221,7 +220,7 @@ repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient
mgconsole_tag="v1.4.0" # (2023-05-21) mgconsole_tag="v1.4.0" # (2023-05-21)
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
spdlog_tag="v1.9.2" # (2021-08-12) spdlog_tag="v1.12.0" # (2022-11-02)
repo_clone_try_double "${primary_urls[spdlog]}" "${secondary_urls[spdlog]}" "spdlog" "$spdlog_tag" true repo_clone_try_double "${primary_urls[spdlog]}" "${secondary_urls[spdlog]}" "spdlog" "$spdlog_tag" true
# librdkafka # librdkafka
@@ -286,5 +285,6 @@ repo_clone_try_double "${primary_urls[range-v3]}" "${secondary_urls[range-v3]}"
nuraft_tag="v2.1.0" nuraft_tag="v2.1.0"
repo_clone_try_double "${primary_urls[nuraft]}" "${secondary_urls[nuraft]}" "nuraft" "$nuraft_tag" true repo_clone_try_double "${primary_urls[nuraft]}" "${secondary_urls[nuraft]}" "nuraft" "$nuraft_tag" true
pushd nuraft pushd nuraft
git apply ../nuraft2.1.0.patch
./prepare.sh ./prepare.sh
popd popd

View File

@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
3. using the Licensed Work to create a work or solution 3. using the Licensed Work to create a work or solution
which competes (or might reasonably be expected to which competes (or might reasonably be expected to
compete) with the Licensed Work. compete) with the Licensed Work.
CHANGE DATE: 2028-21-01 CHANGE DATE: 2028-28-02
CHANGE LICENSE: Apache License, Version 2.0 CHANGE LICENSE: Apache License, Version 2.0
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal. For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.

View File

@@ -35,16 +35,42 @@ DEFINE_VALIDATED_string(auth_module_executable, "", "Absolute path to the auth m
} }
return true; return true;
}); });
DEFINE_bool(auth_module_create_missing_user, true, "Set to false to disable creation of missing users.");
DEFINE_bool(auth_module_create_missing_role, true, "Set to false to disable creation of missing roles.");
DEFINE_bool(auth_module_manage_roles, true, "Set to false to disable management of roles through the auth module.");
DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000, DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
"Timeout (in milliseconds) used when waiting for a " "Timeout (in milliseconds) used when waiting for a "
"response from the auth module.", "response from the auth module.",
FLAG_IN_RANGE(100, 1800000)); FLAG_IN_RANGE(100, 1800000));
// DEPRECATED FLAGS
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
DEFINE_VALIDATED_HIDDEN_bool(auth_module_create_missing_user, true,
"Set to false to disable creation of missing users.", {
spdlog::warn(
"auth_module_create_missing_user flag is deprecated. It not possible to create "
"users through the module anymore.");
return true;
});
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
DEFINE_VALIDATED_HIDDEN_bool(auth_module_create_missing_role, true,
"Set to false to disable creation of missing roles.", {
spdlog::warn(
"auth_module_create_missing_role flag is deprecated. It not possible to create "
"roles through the module anymore.");
return true;
});
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
DEFINE_VALIDATED_HIDDEN_bool(
auth_module_manage_roles, true, "Set to false to disable management of roles through the auth module.", {
spdlog::warn(
"auth_module_manage_roles flag is deprecated. It not possible to create roles through the module anymore.");
return true;
});
namespace memgraph::auth { namespace memgraph::auth {
const Auth::Epoch Auth::kStartEpoch = 1;
namespace { namespace {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
/** /**
@@ -192,6 +218,17 @@ void MigrateVersions(kvstore::KVStore &store) {
version_str = kVersionV1; version_str = kVersionV1;
} }
} }
auto ParseJson(std::string_view str) {
nlohmann::json data;
try {
data = nlohmann::json::parse(str);
} catch (const nlohmann::json::parse_error &e) {
throw AuthException("Couldn't load auth data!");
}
return data;
}
}; // namespace }; // namespace
Auth::Auth(std::string storage_directory, Config config) Auth::Auth(std::string storage_directory, Config config)
@@ -199,8 +236,11 @@ Auth::Auth(std::string storage_directory, Config config)
MigrateVersions(storage_); MigrateVersions(storage_);
} }
std::optional<User> Auth::Authenticate(const std::string &username, const std::string &password) { std::optional<UserOrRole> Auth::Authenticate(const std::string &username, const std::string &password) {
if (module_.IsUsed()) { if (module_.IsUsed()) {
/*
* MODULE AUTH STORAGE
*/
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings); const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
if (license_check_result.HasError()) { if (license_check_result.HasError()) {
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules")); spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
@@ -225,108 +265,64 @@ std::optional<User> Auth::Authenticate(const std::string &username, const std::s
auto is_authenticated = ret_authenticated.get<bool>(); auto is_authenticated = ret_authenticated.get<bool>();
const auto &rolename = ret_role.get<std::string>(); const auto &rolename = ret_role.get<std::string>();
// Check if role is present
auto role = GetRole(rolename);
if (!role) {
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the role '{}' doesn't exist.",
username, rolename, "https://memgr.ph/auth"));
return std::nullopt;
}
// Authenticate the user. // Authenticate the user.
if (!is_authenticated) return std::nullopt; if (!is_authenticated) return std::nullopt;
/** return RoleWUsername{username, std::move(*role)};
* TODO
* The auth module should not update auth data.
* There is now way to replicate it and we should not be storing sensitive data if we don't have to.
*/
// Find or create the user and return it.
auto user = GetUser(username);
if (!user) {
if (FLAGS_auth_module_create_missing_user) {
user = AddUser(username, password);
if (!user) {
spdlog::warn(utils::MessageWithLink(
"Couldn't create the missing user '{}' using the auth module because the user already exists as a role.",
username, "https://memgr.ph/auth"));
return std::nullopt;
}
} else {
spdlog::warn(utils::MessageWithLink(
"Couldn't authenticate user '{}' using the auth module because the user doesn't exist.", username,
"https://memgr.ph/auth"));
return std::nullopt;
}
} else {
UpdatePassword(*user, password);
}
if (FLAGS_auth_module_manage_roles) {
if (!rolename.empty()) {
auto role = GetRole(rolename);
if (!role) {
if (FLAGS_auth_module_create_missing_role) {
role = AddRole(rolename);
if (!role) {
spdlog::warn(
utils::MessageWithLink("Couldn't authenticate user '{}' using the auth module because the user's "
"role '{}' already exists as a user.",
username, rolename, "https://memgr.ph/auth"));
return std::nullopt;
}
SaveRole(*role);
} else {
spdlog::warn(utils::MessageWithLink(
"Couldn't authenticate user '{}' using the auth module because the user's role '{}' doesn't exist.",
username, rolename, "https://memgr.ph/auth"));
return std::nullopt;
}
}
user->SetRole(*role);
} else {
user->ClearRole();
}
}
SaveUser(*user);
return user;
} else {
auto user = GetUser(username);
if (!user) {
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
"https://memgr.ph/auth"));
return std::nullopt;
}
if (!user->CheckPassword(password)) {
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
username, "https://memgr.ph/auth"));
return std::nullopt;
}
if (user->UpgradeHash(password)) {
SaveUser(*user);
}
return user;
} }
/*
* LOCAL AUTH STORAGE
*/
auto user = GetUser(username);
if (!user) {
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
"https://memgr.ph/auth"));
return std::nullopt;
}
if (!user->CheckPassword(password)) {
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
username, "https://memgr.ph/auth"));
return std::nullopt;
}
if (user->UpgradeHash(password)) {
SaveUser(*user);
}
return user;
} }
std::optional<User> Auth::GetUser(const std::string &username_orig) const { void Auth::LinkUser(User &user) const {
auto username = utils::ToLowerCase(username_orig); auto link = storage_.Get(kLinkPrefix + user.username());
auto existing_user = storage_.Get(kUserPrefix + username);
if (!existing_user) return std::nullopt;
nlohmann::json data;
try {
data = nlohmann::json::parse(*existing_user);
} catch (const nlohmann::json::parse_error &e) {
throw AuthException("Couldn't load user data!");
}
auto user = User::Deserialize(data);
auto link = storage_.Get(kLinkPrefix + username);
if (link) { if (link) {
auto role = GetRole(*link); auto role = GetRole(*link);
if (role) { if (role) {
user.SetRole(*role); user.SetRole(*role);
} }
} }
}
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
if (module_.IsUsed()) return std::nullopt; // User's are not supported when using module
auto username = utils::ToLowerCase(username_orig);
auto existing_user = storage_.Get(kUserPrefix + username);
if (!existing_user) return std::nullopt;
auto user = User::Deserialize(ParseJson(*existing_user));
LinkUser(user);
return user; return user;
} }
void Auth::SaveUser(const User &user, system::Transaction *system_tx) { void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
DisableIfModuleUsed();
bool success = false; bool success = false;
if (const auto *role = user.role(); role != nullptr) { if (const auto *role = user.role(); role != nullptr) {
success = storage_.PutMultiple( success = storage_.PutMultiple(
@@ -338,6 +334,10 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
if (!success) { if (!success) {
throw AuthException("Couldn't save user '{}'!", user.username()); throw AuthException("Couldn't save user '{}'!", user.username());
} }
// Durability updated -> new epoch
UpdateEpoch();
// All changes to the user end up calling this function, so no need to add a delta anywhere else // All changes to the user end up calling this function, so no need to add a delta anywhere else
if (system_tx) { if (system_tx) {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
@@ -347,6 +347,7 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
} }
void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) { void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) {
DisableIfModuleUsed();
// Check if null // Check if null
if (!password) { if (!password) {
if (!config_.password_permit_null) { if (!config_.password_permit_null) {
@@ -378,6 +379,7 @@ void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &pa
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password, std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password,
system::Transaction *system_tx) { system::Transaction *system_tx) {
DisableIfModuleUsed();
if (!NameRegexMatch(username)) { if (!NameRegexMatch(username)) {
throw AuthException("Invalid user name."); throw AuthException("Invalid user name.");
} }
@@ -392,12 +394,17 @@ std::optional<User> Auth::AddUser(const std::string &username, const std::option
} }
bool Auth::RemoveUser(const std::string &username_orig, system::Transaction *system_tx) { bool Auth::RemoveUser(const std::string &username_orig, system::Transaction *system_tx) {
DisableIfModuleUsed();
auto username = utils::ToLowerCase(username_orig); auto username = utils::ToLowerCase(username_orig);
if (!storage_.Get(kUserPrefix + username)) return false; if (!storage_.Get(kUserPrefix + username)) return false;
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username}); std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
if (!storage_.DeleteMultiple(keys)) { if (!storage_.DeleteMultiple(keys)) {
throw AuthException("Couldn't remove user '{}'!", username); throw AuthException("Couldn't remove user '{}'!", username);
} }
// Durability updated -> new epoch
UpdateEpoch();
// Handling drop user delta // Handling drop user delta
if (system_tx) { if (system_tx) {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
@@ -412,9 +419,12 @@ std::vector<auth::User> Auth::AllUsers() const {
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) { for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
auto username = it->first.substr(kUserPrefix.size()); auto username = it->first.substr(kUserPrefix.size());
if (username != utils::ToLowerCase(username)) continue; if (username != utils::ToLowerCase(username)) continue;
auto user = GetUser(username); try {
if (user) { User user = auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
ret.push_back(std::move(*user)); LinkUser(user);
ret.emplace_back(std::move(user));
} catch (AuthException &) {
continue;
} }
} }
return ret; return ret;
@@ -425,9 +435,12 @@ std::vector<std::string> Auth::AllUsernames() const {
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) { for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
auto username = it->first.substr(kUserPrefix.size()); auto username = it->first.substr(kUserPrefix.size());
if (username != utils::ToLowerCase(username)) continue; if (username != utils::ToLowerCase(username)) continue;
auto user = GetUser(username); try {
if (user) { // Check if serialized correctly
ret.push_back(username); memgraph::auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
ret.emplace_back(std::move(username));
} catch (AuthException &) {
continue;
} }
} }
return ret; return ret;
@@ -435,25 +448,24 @@ std::vector<std::string> Auth::AllUsernames() const {
bool Auth::HasUsers() const { return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix); } bool Auth::HasUsers() const { return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix); }
bool Auth::AccessControlled() const { return HasUsers() || module_.IsUsed(); }
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const { std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
auto rolename = utils::ToLowerCase(rolename_orig); auto rolename = utils::ToLowerCase(rolename_orig);
auto existing_role = storage_.Get(kRolePrefix + rolename); auto existing_role = storage_.Get(kRolePrefix + rolename);
if (!existing_role) return std::nullopt; if (!existing_role) return std::nullopt;
nlohmann::json data; return Role::Deserialize(ParseJson(*existing_role));
try {
data = nlohmann::json::parse(*existing_role);
} catch (const nlohmann::json::parse_error &e) {
throw AuthException("Couldn't load role data!");
}
return Role::Deserialize(data);
} }
void Auth::SaveRole(const Role &role, system::Transaction *system_tx) { void Auth::SaveRole(const Role &role, system::Transaction *system_tx) {
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) { if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
throw AuthException("Couldn't save role '{}'!", role.rolename()); throw AuthException("Couldn't save role '{}'!", role.rolename());
} }
// Durability updated -> new epoch
UpdateEpoch();
// All changes to the role end up calling this function, so no need to add a delta anywhere else // All changes to the role end up calling this function, so no need to add a delta anywhere else
if (system_tx) { if (system_tx) {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
@@ -486,6 +498,10 @@ bool Auth::RemoveRole(const std::string &rolename_orig, system::Transaction *sys
if (!storage_.DeleteMultiple(keys)) { if (!storage_.DeleteMultiple(keys)) {
throw AuthException("Couldn't remove role '{}'!", rolename); throw AuthException("Couldn't remove role '{}'!", rolename);
} }
// Durability updated -> new epoch
UpdateEpoch();
// Handling drop role delta // Handling drop role delta
if (system_tx) { if (system_tx) {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
@@ -500,11 +516,8 @@ std::vector<auth::Role> Auth::AllRoles() const {
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) { for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
auto rolename = it->first.substr(kRolePrefix.size()); auto rolename = it->first.substr(kRolePrefix.size());
if (rolename != utils::ToLowerCase(rolename)) continue; if (rolename != utils::ToLowerCase(rolename)) continue;
if (auto role = GetRole(rolename)) { Role role = memgraph::auth::Role::Deserialize(ParseJson(it->second)); // Will throw on failure
ret.push_back(*role); ret.emplace_back(std::move(role));
} else {
throw AuthException("Couldn't load role '{}'!", rolename);
}
} }
return ret; return ret;
} }
@@ -514,14 +527,19 @@ std::vector<std::string> Auth::AllRolenames() const {
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) { for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
auto rolename = it->first.substr(kRolePrefix.size()); auto rolename = it->first.substr(kRolePrefix.size());
if (rolename != utils::ToLowerCase(rolename)) continue; if (rolename != utils::ToLowerCase(rolename)) continue;
if (auto role = GetRole(rolename)) { try {
ret.push_back(rolename); // Check that the data is serialized correctly
memgraph::auth::Role::Deserialize(ParseJson(it->second));
ret.emplace_back(std::move(rolename));
} catch (AuthException &) {
continue;
} }
} }
return ret; return ret;
} }
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const { std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
DisableIfModuleUsed();
const auto rolename = utils::ToLowerCase(rolename_orig); const auto rolename = utils::ToLowerCase(rolename_orig);
std::vector<auth::User> ret; std::vector<auth::User> ret;
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) { for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
@@ -540,51 +558,176 @@ std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig)
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx) { Auth::Result Auth::GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
if (auto user = GetUser(name)) { using enum Auth::Result;
if (db == kAllDatabases) { if (module_.IsUsed()) {
user->db_access().GrantAll(); if (auto role = GetRole(name)) {
} else { GrantDatabase(db, *role, system_tx);
user->db_access().Add(db); return SUCCESS;
} }
SaveUser(*user, system_tx); return NO_ROLE;
return true;
} }
return false; if (auto user = GetUser(name)) {
GrantDatabase(db, *user, system_tx);
return SUCCESS;
}
if (auto role = GetRole(name)) {
GrantDatabase(db, *role, system_tx);
return SUCCESS;
}
return NO_USER_ROLE;
} }
bool Auth::RevokeDatabaseFromUser(const std::string &db, const std::string &name, system::Transaction *system_tx) { void Auth::GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
if (auto user = GetUser(name)) { if (db == kAllDatabases) {
if (db == kAllDatabases) { user.db_access().GrantAll();
user->db_access().DenyAll(); } else {
} else { user.db_access().Grant(db);
user->db_access().Remove(db);
}
SaveUser(*user, system_tx);
return true;
} }
return false; SaveUser(user, system_tx);
}
void Auth::GrantDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
if (db == kAllDatabases) {
role.db_access().GrantAll();
} else {
role.db_access().Grant(db);
}
SaveRole(role, system_tx);
}
Auth::Result Auth::DenyDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
using enum Auth::Result;
if (module_.IsUsed()) {
if (auto role = GetRole(name)) {
DenyDatabase(db, *role, system_tx);
return SUCCESS;
}
return NO_ROLE;
}
if (auto user = GetUser(name)) {
DenyDatabase(db, *user, system_tx);
return SUCCESS;
}
if (auto role = GetRole(name)) {
DenyDatabase(db, *role, system_tx);
return SUCCESS;
}
return NO_USER_ROLE;
}
void Auth::DenyDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
if (db == kAllDatabases) {
user.db_access().DenyAll();
} else {
user.db_access().Deny(db);
}
SaveUser(user, system_tx);
}
void Auth::DenyDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
if (db == kAllDatabases) {
role.db_access().DenyAll();
} else {
role.db_access().Deny(db);
}
SaveRole(role, system_tx);
}
Auth::Result Auth::RevokeDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
using enum Auth::Result;
if (module_.IsUsed()) {
if (auto role = GetRole(name)) {
RevokeDatabase(db, *role, system_tx);
return SUCCESS;
}
return NO_ROLE;
}
if (auto user = GetUser(name)) {
RevokeDatabase(db, *user, system_tx);
return SUCCESS;
}
if (auto role = GetRole(name)) {
RevokeDatabase(db, *role, system_tx);
return SUCCESS;
}
return NO_USER_ROLE;
}
void Auth::RevokeDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
if (db == kAllDatabases) {
user.db_access().RevokeAll();
} else {
user.db_access().Revoke(db);
}
SaveUser(user, system_tx);
}
void Auth::RevokeDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
if (db == kAllDatabases) {
role.db_access().RevokeAll();
} else {
role.db_access().Revoke(db);
}
SaveRole(role, system_tx);
} }
void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) { void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) {
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) { for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
auto username = it->first.substr(kUserPrefix.size()); auto username = it->first.substr(kUserPrefix.size());
if (auto user = GetUser(username)) { try {
user->db_access().Delete(db); User user = auth::User::Deserialize(ParseJson(it->second));
SaveUser(*user, system_tx); LinkUser(user);
user.db_access().Revoke(db);
SaveUser(user, system_tx);
} catch (AuthException &) {
continue;
}
}
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
auto rolename = it->first.substr(kRolePrefix.size());
try {
auto role = memgraph::auth::Role::Deserialize(ParseJson(it->second));
role.db_access().Revoke(db);
SaveRole(role, system_tx);
} catch (AuthException &) {
continue;
} }
} }
} }
bool Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) { Auth::Result Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
if (auto user = GetUser(name)) { using enum Auth::Result;
if (!user->db_access().SetDefault(db)) { if (module_.IsUsed()) {
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name); if (auto role = GetRole(name)) {
SetMainDatabase(db, *role, system_tx);
return SUCCESS;
} }
SaveUser(*user, system_tx); return NO_ROLE;
return true;
} }
return false; if (auto user = GetUser(name)) {
SetMainDatabase(db, *user, system_tx);
return SUCCESS;
}
if (auto role = GetRole(name)) {
SetMainDatabase(db, *role, system_tx);
return SUCCESS;
}
return NO_USER_ROLE;
}
void Auth::SetMainDatabase(std::string_view db, User &user, system::Transaction *system_tx) {
if (!user.db_access().SetMain(db)) {
throw AuthException("Couldn't set default database '{}' for '{}'!", db, user.username());
}
SaveUser(user, system_tx);
}
void Auth::SetMainDatabase(std::string_view db, Role &role, system::Transaction *system_tx) {
if (!role.db_access().SetMain(db)) {
throw AuthException("Couldn't set default database '{}' for '{}'!", db, role.rolename());
}
SaveRole(role, system_tx);
} }
#endif #endif

View File

@@ -29,6 +29,18 @@ using SynchedAuth = memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph
static const constexpr char *const kAllDatabases = "*"; static const constexpr char *const kAllDatabases = "*";
struct RoleWUsername : Role {
template <typename... Args>
RoleWUsername(std::string_view username, Args &&...args) : Role{std::forward<Args>(args)...}, username_{username} {}
std::string username() { return username_; }
const std::string &username() const { return username_; }
private:
std::string username_;
};
using UserOrRole = std::variant<User, RoleWUsername>;
/** /**
* This class serves as the main Authentication/Authorization storage. * This class serves as the main Authentication/Authorization storage.
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions. * It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
@@ -61,6 +73,25 @@ class Auth final {
std::regex password_regex{password_regex_str}; std::regex password_regex{password_regex_str};
}; };
struct Epoch {
Epoch() : epoch_{0} {}
Epoch(unsigned e) : epoch_{e} {}
Epoch operator++() { return ++epoch_; }
bool operator==(const Epoch &rhs) const = default;
private:
unsigned epoch_;
};
static const Epoch kStartEpoch;
enum class Result {
SUCCESS,
NO_USER_ROLE,
NO_ROLE,
};
explicit Auth(std::string storage_directory, Config config); explicit Auth(std::string storage_directory, Config config);
/** /**
@@ -89,7 +120,7 @@ class Auth final {
* @return a user when the username and password match, nullopt otherwise * @return a user when the username and password match, nullopt otherwise
* @throw AuthException if unable to authenticate for whatever reason. * @throw AuthException if unable to authenticate for whatever reason.
*/ */
std::optional<User> Authenticate(const std::string &username, const std::string &password); std::optional<UserOrRole> Authenticate(const std::string &username, const std::string &password);
/** /**
* Gets a user from the storage. * Gets a user from the storage.
@@ -101,6 +132,8 @@ class Auth final {
*/ */
std::optional<User> GetUser(const std::string &username) const; std::optional<User> GetUser(const std::string &username) const;
void LinkUser(User &user) const;
/** /**
* Saves a user object to the storage. * Saves a user object to the storage.
* *
@@ -163,6 +196,13 @@ class Auth final {
*/ */
bool HasUsers() const; bool HasUsers() const;
/**
* Returns whether the access is controlled by authentication/authorization.
*
* @return `true` if auth needs to run
*/
bool AccessControlled() const;
/** /**
* Gets a role from the storage. * Gets a role from the storage.
* *
@@ -173,6 +213,37 @@ class Auth final {
*/ */
std::optional<Role> GetRole(const std::string &rolename) const; std::optional<Role> GetRole(const std::string &rolename) const;
std::optional<UserOrRole> GetUserOrRole(const std::optional<std::string> &username,
const std::optional<std::string> &rolename) const {
auto expect = [](bool condition, std::string &&msg) {
if (!condition) throw AuthException(std::move(msg));
};
// Special case if we are using a module; we must find the specified role
if (module_.IsUsed()) {
expect(username && rolename, "When using a module, a role needs to be connected to a username.");
const auto role = GetRole(*rolename);
expect(role != std::nullopt, "No role named " + *rolename);
return UserOrRole(auth::RoleWUsername{*username, *role});
}
// First check if we need to find a role
if (username && rolename) {
const auto role = GetRole(*rolename);
expect(role != std::nullopt, "No role named " + *rolename);
return UserOrRole(auth::RoleWUsername{*username, *role});
}
// We are only looking for a user
if (username) {
const auto user = GetUser(*username);
expect(user != std::nullopt, "No user named " + *username);
return *user;
}
// No user or role
return std::nullopt;
}
/** /**
* Saves a role object to the storage. * Saves a role object to the storage.
* *
@@ -229,16 +300,6 @@ class Auth final {
std::vector<User> AllUsersForRole(const std::string &rolename) const; std::vector<User> AllUsersForRole(const std::string &rolename) const;
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
/**
* @brief Revoke access to individual database for a user.
*
* @param db name of the database to revoke
* @param name user's username
* @return true on success
* @throw AuthException if unable to find or update the user
*/
bool RevokeDatabaseFromUser(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
/** /**
* @brief Grant access to individual database for a user. * @brief Grant access to individual database for a user.
* *
@@ -247,7 +308,33 @@ class Auth final {
* @return true on success * @return true on success
* @throw AuthException if unable to find or update the user * @throw AuthException if unable to find or update the user
*/ */
bool GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr); Result GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
void GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
void GrantDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
/**
* @brief Revoke access to individual database for a user.
*
* @param db name of the database to revoke
* @param name user's username
* @return true on success
* @throw AuthException if unable to find or update the user
*/
Result DenyDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
void DenyDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
void DenyDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
/**
* @brief Revoke access to individual database for a user.
*
* @param db name of the database to revoke
* @param name user's username
* @return true on success
* @throw AuthException if unable to find or update the user
*/
Result RevokeDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
void RevokeDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
void RevokeDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
/** /**
* @brief Delete a database from all users. * @brief Delete a database from all users.
@@ -265,9 +352,17 @@ class Auth final {
* @return true on success * @return true on success
* @throw AuthException if unable to find or update the user * @throw AuthException if unable to find or update the user
*/ */
bool SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx = nullptr); Result SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx = nullptr);
void SetMainDatabase(std::string_view db, User &user, system::Transaction *system_tx = nullptr);
void SetMainDatabase(std::string_view db, Role &role, system::Transaction *system_tx = nullptr);
#endif #endif
bool UpToDate(Epoch &e) const {
bool res = e == epoch_;
e = epoch_;
return res;
}
private: private:
/** /**
* @brief * @brief
@@ -278,11 +373,18 @@ class Auth final {
*/ */
bool NameRegexMatch(const std::string &user_or_role) const; bool NameRegexMatch(const std::string &user_or_role) const;
void UpdateEpoch() { ++epoch_; }
void DisableIfModuleUsed() const {
if (module_.IsUsed()) throw AuthException("Operation not permited when using an authentication module.");
}
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe, // Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
// Auth is not thread-safe because modifying users and roles might require // Auth is not thread-safe because modifying users and roles might require
// more than one operation on the storage. // more than one operation on the storage.
kvstore::KVStore storage_; kvstore::KVStore storage_;
auth::Module module_; auth::Module module_;
Config config_; Config config_;
Epoch epoch_{kStartEpoch};
}; };
} // namespace memgraph::auth } // namespace memgraph::auth

View File

@@ -8,10 +8,12 @@
#pragma once #pragma once
#include <json/json.hpp> #include <cstdint>
#include <optional> #include <optional>
#include <string> #include <string>
#include <json/json.hpp>
namespace memgraph::auth { namespace memgraph::auth {
/// Need to be stable, auth durability depends on this /// Need to be stable, auth durability depends on this
enum class PasswordHashAlgorithm : uint8_t { BCRYPT = 0, SHA256 = 1, SHA256_MULTIPLE = 2 }; enum class PasswordHashAlgorithm : uint8_t { BCRYPT = 0, SHA256 = 1, SHA256_MULTIPLE = 2 };

View File

@@ -425,10 +425,11 @@ Role::Role(const std::string &rolename, const Permissions &permissions)
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {} : rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
Role::Role(const std::string &rolename, const Permissions &permissions, Role::Role(const std::string &rolename, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler) FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
: rolename_(utils::ToLowerCase(rolename)), : rolename_(utils::ToLowerCase(rolename)),
permissions_(permissions), permissions_(permissions),
fine_grained_access_handler_(std::move(fine_grained_access_handler)) {} fine_grained_access_handler_(std::move(fine_grained_access_handler)),
db_access_(std::move(db_access)) {}
#endif #endif
const std::string &Role::rolename() const { return rolename_; } const std::string &Role::rolename() const { return rolename_; }
@@ -454,8 +455,10 @@ nlohmann::json Role::Serialize() const {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize(); data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
data[kDatabases] = db_access_.Serialize();
} else { } else {
data[kFineGrainedAccessHandler] = {}; data[kFineGrainedAccessHandler] = {};
data[kDatabases] = {};
} }
#endif #endif
return data; return data;
@@ -471,12 +474,21 @@ Role Role::Deserialize(const nlohmann::json &data) {
auto permissions = Permissions::Deserialize(data[kPermissions]); auto permissions = Permissions::Deserialize(data[kPermissions]);
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
Databases db_access;
if (data[kDatabases].is_structured()) {
db_access = Databases::Deserialize(data[kDatabases]);
} else {
// Back-compatibility
spdlog::warn("Role without specified database access. Given access to the default database.");
db_access.Grant(dbms::kDefaultDB);
db_access.SetMain(dbms::kDefaultDB);
}
FineGrainedAccessHandler fine_grained_access_handler; FineGrainedAccessHandler fine_grained_access_handler;
// We can have an empty fine_grained if the user was created without a valid license // We can have an empty fine_grained if the user was created without a valid license
if (data[kFineGrainedAccessHandler].is_object()) { if (data[kFineGrainedAccessHandler].is_object()) {
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]); fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
} }
return {data[kRoleName], permissions, std::move(fine_grained_access_handler)}; return {data[kRoleName], permissions, std::move(fine_grained_access_handler), std::move(db_access)};
} }
#endif #endif
return {data[kRoleName], permissions}; return {data[kRoleName], permissions};
@@ -493,7 +505,7 @@ bool operator==(const Role &first, const Role &second) {
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
void Databases::Add(std::string_view db) { void Databases::Grant(std::string_view db) {
if (allow_all_) { if (allow_all_) {
grants_dbs_.clear(); grants_dbs_.clear();
allow_all_ = false; allow_all_ = false;
@@ -502,19 +514,19 @@ void Databases::Add(std::string_view db) {
denies_dbs_.erase(std::string{db}); // TODO: C++23 use transparent key compare denies_dbs_.erase(std::string{db}); // TODO: C++23 use transparent key compare
} }
void Databases::Remove(const std::string &db) { void Databases::Deny(const std::string &db) {
denies_dbs_.emplace(db); denies_dbs_.emplace(db);
grants_dbs_.erase(db); grants_dbs_.erase(db);
} }
void Databases::Delete(const std::string &db) { void Databases::Revoke(const std::string &db) {
denies_dbs_.erase(db); denies_dbs_.erase(db);
if (!allow_all_) { if (!allow_all_) {
grants_dbs_.erase(db); grants_dbs_.erase(db);
} }
// Reset if default deleted // Reset if default deleted
if (default_db_ == db) { if (main_db_ == db) {
default_db_ = ""; main_db_ = "";
} }
} }
@@ -530,9 +542,16 @@ void Databases::DenyAll() {
denies_dbs_.clear(); denies_dbs_.clear();
} }
bool Databases::SetDefault(std::string_view db) { void Databases::RevokeAll() {
allow_all_ = false;
grants_dbs_.clear();
denies_dbs_.clear();
main_db_ = "";
}
bool Databases::SetMain(std::string_view db) {
if (!Contains(db)) return false; if (!Contains(db)) return false;
default_db_ = db; main_db_ = db;
return true; return true;
} }
@@ -540,11 +559,11 @@ bool Databases::SetDefault(std::string_view db) {
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db)); return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
} }
const std::string &Databases::GetDefault() const { const std::string &Databases::GetMain() const {
if (!Contains(default_db_)) { if (!Contains(main_db_)) {
throw AuthException("No access to the set default database \"{}\".", default_db_); throw AuthException("No access to the set default database \"{}\".", main_db_);
} }
return default_db_; return main_db_;
} }
nlohmann::json Databases::Serialize() const { nlohmann::json Databases::Serialize() const {
@@ -552,7 +571,7 @@ nlohmann::json Databases::Serialize() const {
data[kGrants] = grants_dbs_; data[kGrants] = grants_dbs_;
data[kDenies] = denies_dbs_; data[kDenies] = denies_dbs_;
data[kAllowAll] = allow_all_; data[kAllowAll] = allow_all_;
data[kDefault] = default_db_; data[kDefault] = main_db_;
return data; return data;
} }
@@ -719,15 +738,16 @@ User User::Deserialize(const nlohmann::json &data) {
} else { } else {
// Back-compatibility // Back-compatibility
spdlog::warn("User without specified database access. Given access to the default database."); spdlog::warn("User without specified database access. Given access to the default database.");
db_access.Add(dbms::kDefaultDB); db_access.Grant(dbms::kDefaultDB);
db_access.SetDefault(dbms::kDefaultDB); db_access.SetMain(dbms::kDefaultDB);
} }
FineGrainedAccessHandler fine_grained_access_handler; FineGrainedAccessHandler fine_grained_access_handler;
// We can have an empty fine_grained if the user was created without a valid license // We can have an empty fine_grained if the user was created without a valid license
if (data[kFineGrainedAccessHandler].is_object()) { if (data[kFineGrainedAccessHandler].is_object()) {
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]); fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
} }
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler), db_access}; return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler),
std::move(db_access)};
} }
#endif #endif
return {data[kUsername], std::move(password_hash), permissions}; return {data[kUsername], std::move(password_hash), permissions};

View File

@@ -205,52 +205,10 @@ class FineGrainedAccessHandler final {
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second); bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
#endif #endif
class Role final {
public:
Role() = default;
explicit Role(const std::string &rolename);
Role(const std::string &rolename, const Permissions &permissions);
#ifdef MG_ENTERPRISE
Role(const std::string &rolename, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler);
#endif
Role(const Role &) = default;
Role &operator=(const Role &) = default;
Role(Role &&) noexcept = default;
Role &operator=(Role &&) noexcept = default;
~Role() = default;
const std::string &rolename() const;
const Permissions &permissions() const;
Permissions &permissions();
#ifdef MG_ENTERPRISE
const FineGrainedAccessHandler &fine_grained_access_handler() const;
FineGrainedAccessHandler &fine_grained_access_handler();
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
#endif
nlohmann::json Serialize() const;
/// @throw AuthException if unable to deserialize.
static Role Deserialize(const nlohmann::json &data);
friend bool operator==(const Role &first, const Role &second);
private:
std::string rolename_;
Permissions permissions_;
#ifdef MG_ENTERPRISE
FineGrainedAccessHandler fine_grained_access_handler_;
#endif
};
bool operator==(const Role &first, const Role &second);
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
class Databases final { class Databases final {
public: public:
Databases() : grants_dbs_{std::string{dbms::kDefaultDB}}, allow_all_(false), default_db_(dbms::kDefaultDB) {} Databases() : grants_dbs_{std::string{dbms::kDefaultDB}}, allow_all_(false), main_db_(dbms::kDefaultDB) {}
Databases(const Databases &) = default; Databases(const Databases &) = default;
Databases &operator=(const Databases &) = default; Databases &operator=(const Databases &) = default;
@@ -263,7 +221,7 @@ class Databases final {
* *
* @param db name of the database to grant access to * @param db name of the database to grant access to
*/ */
void Add(std::string_view db); void Grant(std::string_view db);
/** /**
* @brief Remove database to the list of granted access. * @brief Remove database to the list of granted access.
@@ -272,7 +230,7 @@ class Databases final {
* *
* @param db name of the database to grant access to * @param db name of the database to grant access to
*/ */
void Remove(const std::string &db); void Deny(const std::string &db);
/** /**
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set. * @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
@@ -280,7 +238,7 @@ class Databases final {
* *
* @param db name of the database to grant access to * @param db name of the database to grant access to
*/ */
void Delete(const std::string &db); void Revoke(const std::string &db);
/** /**
* @brief Set allow_all_ to true and clears grants and denied sets. * @brief Set allow_all_ to true and clears grants and denied sets.
@@ -292,10 +250,15 @@ class Databases final {
*/ */
void DenyAll(); void DenyAll();
/**
* @brief Set allow_all_ to false and clears grants and denied sets.
*/
void RevokeAll();
/** /**
* @brief Set the default database. * @brief Set the default database.
*/ */
bool SetDefault(std::string_view db); bool SetMain(std::string_view db);
/** /**
* @brief Checks if access is grated to the database. * @brief Checks if access is grated to the database.
@@ -304,11 +267,13 @@ class Databases final {
* @return true if allow_all and not denied or granted * @return true if allow_all and not denied or granted
*/ */
bool Contains(std::string_view db) const; bool Contains(std::string_view db) const;
bool Denies(std::string_view db_name) const { return denies_dbs_.contains(db_name); }
bool Grants(std::string_view db_name) const { return allow_all_ || grants_dbs_.contains(db_name); }
bool GetAllowAll() const { return allow_all_; } bool GetAllowAll() const { return allow_all_; }
const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; } const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; }
const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; } const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; }
const std::string &GetDefault() const; const std::string &GetMain() const;
nlohmann::json Serialize() const; nlohmann::json Serialize() const;
/// @throw AuthException if unable to deserialize. /// @throw AuthException if unable to deserialize.
@@ -320,15 +285,69 @@ class Databases final {
: grants_dbs_(std::move(grant)), : grants_dbs_(std::move(grant)),
denies_dbs_(std::move(deny)), denies_dbs_(std::move(deny)),
allow_all_(allow_all), allow_all_(allow_all),
default_db_(std::move(default_db)) {} main_db_(std::move(default_db)) {}
std::set<std::string, std::less<>> grants_dbs_; //!< set of databases with granted access std::set<std::string, std::less<>> grants_dbs_; //!< set of databases with granted access
std::set<std::string, std::less<>> denies_dbs_; //!< set of databases with denied access std::set<std::string, std::less<>> denies_dbs_; //!< set of databases with denied access
bool allow_all_; //!< flag to allow access to everything (denied overrides this) bool allow_all_; //!< flag to allow access to everything (denied overrides this)
std::string default_db_; //!< user's default database std::string main_db_; //!< user's default database
}; };
#endif #endif
class Role {
public:
Role() = default;
explicit Role(const std::string &rolename);
Role(const std::string &rolename, const Permissions &permissions);
#ifdef MG_ENTERPRISE
Role(const std::string &rolename, const Permissions &permissions,
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
#endif
Role(const Role &) = default;
Role &operator=(const Role &) = default;
Role(Role &&) noexcept = default;
Role &operator=(Role &&) noexcept = default;
~Role() = default;
const std::string &rolename() const;
const Permissions &permissions() const;
Permissions &permissions();
Permissions GetPermissions() const { return permissions_; }
#ifdef MG_ENTERPRISE
const FineGrainedAccessHandler &fine_grained_access_handler() const;
FineGrainedAccessHandler &fine_grained_access_handler();
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
#endif
#ifdef MG_ENTERPRISE
Databases &db_access() { return db_access_; }
const Databases &db_access() const { return db_access_; }
bool DeniesDB(std::string_view db_name) const { return db_access_.Denies(db_name); }
bool GrantsDB(std::string_view db_name) const { return db_access_.Grants(db_name); }
bool HasAccess(std::string_view db_name) const { return !DeniesDB(db_name) && GrantsDB(db_name); }
#endif
nlohmann::json Serialize() const;
/// @throw AuthException if unable to deserialize.
static Role Deserialize(const nlohmann::json &data);
friend bool operator==(const Role &first, const Role &second);
private:
std::string rolename_;
Permissions permissions_;
#ifdef MG_ENTERPRISE
FineGrainedAccessHandler fine_grained_access_handler_;
Databases db_access_;
#endif
};
bool operator==(const Role &first, const Role &second);
// TODO (mferencevic): Implement password expiry. // TODO (mferencevic): Implement password expiry.
class User final { class User final {
public: public:
@@ -388,6 +407,18 @@ class User final {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
Databases &db_access() { return database_access_; } Databases &db_access() { return database_access_; }
const Databases &db_access() const { return database_access_; } const Databases &db_access() const { return database_access_; }
bool DeniesDB(std::string_view db_name) const {
bool denies = database_access_.Denies(db_name);
if (role_) denies |= role_->DeniesDB(db_name);
return denies;
}
bool GrantsDB(std::string_view db_name) const {
bool grants = database_access_.Grants(db_name);
if (role_) grants |= role_->GrantsDB(db_name);
return grants;
}
bool HasAccess(std::string_view db_name) const { return !DeniesDB(db_name) && GrantsDB(db_name); }
#endif #endif
nlohmann::json Serialize() const; nlohmann::json Serialize() const;
@@ -403,7 +434,7 @@ class User final {
Permissions permissions_; Permissions permissions_;
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
FineGrainedAccessHandler fine_grained_access_handler_; FineGrainedAccessHandler fine_grained_access_handler_;
Databases database_access_; Databases database_access_{};
#endif #endif
std::optional<Role> role_; std::optional<Role> role_;
}; };

View File

@@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise // Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use // License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -403,7 +403,7 @@ nlohmann::json Module::Call(const nlohmann::json &params, int timeout_millisec)
return ret; return ret;
} }
bool Module::IsUsed() { return !module_executable_path_.empty(); } bool Module::IsUsed() const { return !module_executable_path_.empty(); }
void Module::Shutdown() { void Module::Shutdown() {
if (pid_ == -1) return; if (pid_ == -1) return;

View File

@@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise // Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use // License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
@@ -49,7 +49,7 @@ class Module final {
/// specified executable path and can thus be used. /// specified executable path and can thus be used.
/// ///
/// @return boolean indicating whether the module can be used /// @return boolean indicating whether the module can be used
bool IsUsed(); bool IsUsed() const;
~Module(); ~Module();

View File

@@ -18,11 +18,9 @@
#include "utils/enum.hpp" #include "utils/enum.hpp"
namespace memgraph::slk { namespace memgraph::slk {
// Serialize code for auth::Role // Serialize code for auth::Role
void Save(const auth::Role &self, memgraph::slk::Builder *builder) { void Save(const auth::Role &self, Builder *builder) { memgraph::slk::Save(self.Serialize().dump(), builder); }
memgraph::slk::Save(self.Serialize().dump(), builder);
}
namespace { namespace {
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) { auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
std::string tmp; std::string tmp;

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -15,6 +15,9 @@
#include "communication/bolt/v1/value.hpp" #include "communication/bolt/v1/value.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "communication/bolt/v1/fmt.hpp"
#include "io/network/fmt.hpp"
namespace { namespace {
constexpr uint8_t kBoltV43Version[4] = {0x00, 0x00, 0x03, 0x04}; constexpr uint8_t kBoltV43Version[4] = {0x00, 0x00, 0x03, 0x04};
constexpr uint8_t kEmptyBoltVersion[4] = {0x00, 0x00, 0x00, 0x00}; constexpr uint8_t kEmptyBoltVersion[4] = {0x00, 0x00, 0x00, 0x00};

View File

@@ -0,0 +1,27 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#if FMT_VERSION > 90000
#include <fmt/ostream.h>
#include "communication/bolt/v1/value.hpp"
template <>
class fmt::formatter<memgraph::communication::bolt::Value> : public fmt::ostream_formatter {};
template <>
class fmt::formatter<std::vector<memgraph::communication::bolt::Value>> : public fmt::ostream_formatter {};
template <>
class fmt::formatter<std::map<std::string, memgraph::communication::bolt::Value>> : public fmt::ostream_formatter {};
#endif

20
src/communication/fmt.hpp Normal file
View File

@@ -0,0 +1,20 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#if FMT_VERSION > 90000
#include <fmt/ostream.h>
#include <boost/asio/ip/tcp.hpp>
template <>
class fmt::formatter<boost::asio::ip::tcp::endpoint> : public fmt::ostream_formatter {};
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -21,6 +21,7 @@
#include <boost/beast/core.hpp> #include <boost/beast/core.hpp>
#include "communication/context.hpp" #include "communication/context.hpp"
#include "communication/fmt.hpp"
#include "communication/http/session.hpp" #include "communication/http/session.hpp"
#include "utils/spin_lock.hpp" #include "utils/spin_lock.hpp"
#include "utils/synchronized.hpp" #include "utils/synchronized.hpp"
@@ -82,7 +83,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
return; return;
} }
spdlog::info("HTTP server is listening on {}:{}", endpoint.address(), endpoint.port()); spdlog::info("HTTP server is listening on {}", endpoint);
} }
void DoAccept() { void DoAccept() {

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -23,6 +23,7 @@
#include "communication/session.hpp" #include "communication/session.hpp"
#include "io/network/epoll.hpp" #include "io/network/epoll.hpp"
#include "io/network/fmt.hpp"
#include "io/network/socket.hpp" #include "io/network/socket.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/signals.hpp" #include "utils/signals.hpp"

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -22,6 +22,7 @@
#include "communication/init.hpp" #include "communication/init.hpp"
#include "communication/listener.hpp" #include "communication/listener.hpp"
#include "io/network/fmt.hpp"
#include "io/network/socket.hpp" #include "io/network/socket.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/message.hpp" #include "utils/message.hpp"

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -26,6 +26,7 @@
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include "communication/context.hpp" #include "communication/context.hpp"
#include "communication/fmt.hpp"
#include "communication/init.hpp" #include "communication/init.hpp"
#include "communication/v2/listener.hpp" #include "communication/v2/listener.hpp"
#include "communication/v2/pool.hpp" #include "communication/v2/pool.hpp"
@@ -129,7 +130,7 @@ bool Server<TSession, TSessionContext>::Start() {
listener_->Start(); listener_->Start();
spdlog::info("{} server is fully armed and operational", service_name_); spdlog::info("{} server is fully armed and operational", service_name_);
spdlog::info("{} listening on {}", service_name_, endpoint_.address()); spdlog::info("{} listening on {}", service_name_, endpoint_);
context_thread_pool_.Run(); context_thread_pool_.Run();
return true; return true;

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -47,6 +47,7 @@
#include "communication/buffer.hpp" #include "communication/buffer.hpp"
#include "communication/context.hpp" #include "communication/context.hpp"
#include "communication/exceptions.hpp" #include "communication/exceptions.hpp"
#include "communication/fmt.hpp"
#include "dbms/global.hpp" #include "dbms/global.hpp"
#include "utils/event_counter.hpp" #include "utils/event_counter.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
@@ -212,14 +213,11 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
session_.Execute(); session_.Execute();
DoRead(); DoRead();
} catch (const SessionClosedException &e) { } catch (const SessionClosedException &e) {
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(), spdlog::info("{} client {} closed the connection.", service_name_, remote_endpoint_);
remote_endpoint_.port());
DoClose(); DoClose();
} catch (const std::exception &e) { } catch (const std::exception &e) {
spdlog::error( spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
"Exception was thrown while processing event in {} session " remote_endpoint_);
"associated with {}:{}",
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
spdlog::debug("Exception message: {}", e.what()); spdlog::debug("Exception message: {}", e.what());
DoClose(); DoClose();
} }
@@ -376,8 +374,7 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
socket.lowest_layer().non_blocking(false); socket.lowest_layer().non_blocking(false);
}); });
timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max()); timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max());
spdlog::info("Accepted a connection from {}: {}:{}", service_name_, remote_endpoint_.address(), spdlog::info("Accepted a connection from {}: {}", service_name_, remote_endpoint_);
remote_endpoint_.port());
} }
void DoRead() { void DoRead() {
@@ -437,14 +434,11 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
session_.Execute(); session_.Execute();
DoRead(); DoRead();
} catch (const SessionClosedException &e) { } catch (const SessionClosedException &e) {
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(), spdlog::info("{} client {} closed the connection.", service_name_, remote_endpoint_);
remote_endpoint_.port());
DoShutdown(); DoShutdown();
} catch (const std::exception &e) { } catch (const std::exception &e) {
spdlog::error( spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
"Exception was thrown while processing event in {} session " remote_endpoint_);
"associated with {}:{}",
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
spdlog::debug("Exception message: {}", e.what()); spdlog::debug("Exception message: {}", e.what());
DoShutdown(); DoShutdown();
} }

View File

@@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -12,19 +12,44 @@
#include "communication/websocket/auth.hpp" #include "communication/websocket/auth.hpp"
#include <string> #include <string>
#include "utils/variant_helpers.hpp"
namespace memgraph::communication::websocket { namespace memgraph::communication::websocket {
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const { bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
return auth_->Lock()->Authenticate(username, password).has_value(); user_or_role_ = auth_->Lock()->Authenticate(username, password);
return user_or_role_.has_value();
} }
bool SafeAuth::HasUserPermission(const std::string &username, const auth::Permission permission) const { bool SafeAuth::HasPermission(const auth::Permission permission) const {
if (const auto user = auth_->ReadLock()->GetUser(username); user) { auto locked_auth = auth_->ReadLock();
return user->GetPermissions().Has(permission) == auth::PermissionLevel::GRANT; // Update if cache invalidated
if (!locked_auth->UpToDate(auth_epoch_) && user_or_role_) {
bool success = true;
std::visit(utils::Overloaded{[&](auth::User &user) {
auto tmp = locked_auth->GetUser(user.username());
if (!tmp) success = false;
user = std::move(*tmp);
},
[&](auth::Role &role) {
auto tmp = locked_auth->GetRole(role.rolename());
if (!tmp) success = false;
role = std::move(*tmp);
}},
*user_or_role_);
// Missing user/role; delete from cache
if (!success) user_or_role_.reset();
} }
// Check permissions
if (user_or_role_) {
return std::visit(utils::Overloaded{[&](auto &user_or_role) {
return user_or_role.GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
}},
*user_or_role_);
}
// NOTE: websocket authenticates only if there is a user, so no need to check if access controlled
return false; return false;
} }
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); } bool SafeAuth::AccessControlled() const { return auth_->ReadLock()->AccessControlled(); }
} // namespace memgraph::communication::websocket } // namespace memgraph::communication::websocket

View File

@@ -21,9 +21,9 @@ class AuthenticationInterface {
public: public:
virtual bool Authenticate(const std::string &username, const std::string &password) const = 0; virtual bool Authenticate(const std::string &username, const std::string &password) const = 0;
virtual bool HasUserPermission(const std::string &username, auth::Permission permission) const = 0; virtual bool HasPermission(auth::Permission permission) const = 0;
virtual bool HasAnyUsers() const = 0; virtual bool AccessControlled() const = 0;
}; };
class SafeAuth : public AuthenticationInterface { class SafeAuth : public AuthenticationInterface {
@@ -32,11 +32,13 @@ class SafeAuth : public AuthenticationInterface {
bool Authenticate(const std::string &username, const std::string &password) const override; bool Authenticate(const std::string &username, const std::string &password) const override;
bool HasUserPermission(const std::string &username, auth::Permission permission) const override; bool HasPermission(auth::Permission permission) const override;
bool HasAnyUsers() const override; bool AccessControlled() const override;
private: private:
auth::SynchedAuth *auth_; auth::SynchedAuth *auth_;
mutable std::optional<auth::UserOrRole> user_or_role_;
mutable auth::Auth::Epoch auth_epoch_{};
}; };
} // namespace memgraph::communication::websocket } // namespace memgraph::communication::websocket

View File

@@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -10,6 +10,7 @@
// licenses/APL.txt. // licenses/APL.txt.
#include "communication/websocket/listener.hpp" #include "communication/websocket/listener.hpp"
#include "communication/fmt.hpp"
namespace memgraph::communication::websocket { namespace memgraph::communication::websocket {
namespace { namespace {
@@ -61,7 +62,7 @@ Listener::Listener(boost::asio::io_context &ioc, ServerContext *context, tcp::en
return; return;
} }
spdlog::info("WebSocket server is listening on {}:{}", endpoint.address(), endpoint.port()); spdlog::info("WebSocket server is listening on {}", endpoint);
} }
void Listener::DoAccept() { void Listener::DoAccept() {

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -80,7 +80,7 @@ bool Session::Run() {
return false; return false;
} }
authenticated_ = !auth_.HasAnyUsers(); authenticated_ = !auth_.AccessControlled();
connected_.store(true, std::memory_order_relaxed); connected_.store(true, std::memory_order_relaxed);
// run on the strand // run on the strand
@@ -162,7 +162,7 @@ utils::BasicResult<std::string> Session::Authorize(const nlohmann::json &creds)
return {"Authentication failed!"}; return {"Authentication failed!"};
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
if (!auth_.HasUserPermission(creds.at("username").get<std::string>(), auth::Permission::WEBSOCKET)) { if (!auth_.HasPermission(auth::Permission::WEBSOCKET)) {
return {"Authorization failed!"}; return {"Authorization failed!"};
} }
#endif #endif

View File

@@ -11,10 +11,10 @@ target_sources(mg-coordination
include/coordination/coordinator_slk.hpp include/coordination/coordinator_slk.hpp
include/coordination/coordinator_instance.hpp include/coordination/coordinator_instance.hpp
include/coordination/coordinator_handlers.hpp include/coordination/coordinator_handlers.hpp
include/coordination/constants.hpp
include/coordination/instance_status.hpp include/coordination/instance_status.hpp
include/coordination/replication_instance.hpp include/coordination/replication_instance.hpp
include/coordination/raft_state.hpp include/coordination/raft_state.hpp
include/coordination/rpc_errors.hpp
include/nuraft/coordinator_log_store.hpp include/nuraft/coordinator_log_store.hpp
include/nuraft/coordinator_state_machine.hpp include/nuraft/coordinator_state_machine.hpp

View File

@@ -17,6 +17,7 @@
#include "coordination/coordinator_config.hpp" #include "coordination/coordinator_config.hpp"
#include "coordination/coordinator_rpc.hpp" #include "coordination/coordinator_rpc.hpp"
#include "replication_coordination_glue/messages.hpp" #include "replication_coordination_glue/messages.hpp"
#include "utils/result.hpp"
namespace memgraph::coordination { namespace memgraph::coordination {
@@ -45,6 +46,10 @@ auto CoordinatorClient::InstanceDownTimeoutSec() const -> std::chrono::seconds {
return config_.instance_down_timeout_sec; return config_.instance_down_timeout_sec;
} }
auto CoordinatorClient::InstanceGetUUIDFrequencySec() const -> std::chrono::seconds {
return config_.instance_get_uuid_frequency_sec;
}
void CoordinatorClient::StartFrequentCheck() { void CoordinatorClient::StartFrequentCheck() {
if (instance_checker_.IsRunning()) { if (instance_checker_.IsRunning()) {
return; return;
@@ -140,5 +145,31 @@ auto CoordinatorClient::SendUnregisterReplicaRpc(std::string const &instance_nam
return false; return false;
} }
auto CoordinatorClient::SendGetInstanceUUIDRpc() const
-> utils::BasicResult<GetInstanceUUIDError, std::optional<utils::UUID>> {
try {
auto stream{rpc_client_.Stream<GetInstanceUUIDRpc>()};
auto res = stream.AwaitResponse();
return res.uuid;
} catch (const rpc::RpcFailedException &) {
spdlog::error("RPC error occured while sending GetInstance UUID RPC");
return GetInstanceUUIDError::RPC_EXCEPTION;
}
}
auto CoordinatorClient::SendEnableWritingOnMainRpc() const -> bool {
try {
auto stream{rpc_client_.Stream<EnableWritingOnMainRpc>()};
if (!stream.AwaitResponse().success) {
spdlog::error("Failed to receive successful RPC response for enabling writing on main!");
return false;
}
return true;
} catch (rpc::RpcFailedException const &) {
spdlog::error("Failed to enable writing on main!");
}
return false;
}
} // namespace memgraph::coordination } // namespace memgraph::coordination
#endif #endif

View File

@@ -45,6 +45,18 @@ void CoordinatorHandlers::Register(memgraph::coordination::CoordinatorServer &se
spdlog::info("Received UnregisterReplicaRpc on coordinator server"); spdlog::info("Received UnregisterReplicaRpc on coordinator server");
CoordinatorHandlers::UnregisterReplicaHandler(replication_handler, req_reader, res_builder); CoordinatorHandlers::UnregisterReplicaHandler(replication_handler, req_reader, res_builder);
}); });
server.Register<coordination::EnableWritingOnMainRpc>(
[&replication_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
spdlog::info("Received EnableWritingOnMainRpc on coordinator server");
CoordinatorHandlers::EnableWritingOnMainHandler(replication_handler, req_reader, res_builder);
});
server.Register<coordination::GetInstanceUUIDRpc>(
[&replication_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
spdlog::info("Received GetInstanceUUIDRpc on coordinator server");
CoordinatorHandlers::GetInstanceUUIDHandler(replication_handler, req_reader, res_builder);
});
} }
void CoordinatorHandlers::SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler, void CoordinatorHandlers::SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler,
@@ -68,12 +80,6 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
slk::Reader *req_reader, slk::Builder *res_builder) { slk::Reader *req_reader, slk::Builder *res_builder) {
spdlog::info("Executing DemoteMainToReplicaHandler"); spdlog::info("Executing DemoteMainToReplicaHandler");
if (!replication_handler.IsMain()) {
spdlog::error("Setting to replica must be performed on main.");
slk::Save(coordination::DemoteMainToReplicaRes{false}, res_builder);
return;
}
coordination::DemoteMainToReplicaReq req; coordination::DemoteMainToReplicaReq req;
slk::Load(&req, req_reader); slk::Load(&req, req_reader);
@@ -83,11 +89,18 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
if (!replication_handler.SetReplicationRoleReplica(clients_config, std::nullopt)) { if (!replication_handler.SetReplicationRoleReplica(clients_config, std::nullopt)) {
spdlog::error("Demoting main to replica failed!"); spdlog::error("Demoting main to replica failed!");
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder); slk::Save(coordination::DemoteMainToReplicaRes{false}, res_builder);
return; return;
} }
slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder); slk::Save(coordination::DemoteMainToReplicaRes{true}, res_builder);
}
void CoordinatorHandlers::GetInstanceUUIDHandler(replication::ReplicationHandler &replication_handler,
slk::Reader * /*req_reader*/, slk::Builder *res_builder) {
spdlog::info("Executing GetInstanceUUIDHandler");
slk::Save(coordination::GetInstanceUUIDRes{replication_handler.GetReplicaUUID()}, res_builder);
} }
void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHandler &replication_handler, void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHandler &replication_handler,
@@ -119,7 +132,7 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
// registering replicas // registering replicas
for (auto const &config : req.replication_clients_info | ranges::views::transform(converter)) { for (auto const &config : req.replication_clients_info | ranges::views::transform(converter)) {
auto instance_client = replication_handler.RegisterReplica(config, false); auto instance_client = replication_handler.RegisterReplica(config);
if (instance_client.HasError()) { if (instance_client.HasError()) {
using enum memgraph::replication::RegisterReplicaError; using enum memgraph::replication::RegisterReplicaError;
switch (instance_client.GetError()) { switch (instance_client.GetError()) {
@@ -148,7 +161,7 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
} }
} }
} }
spdlog::error(fmt::format("FICO : Promote replica to main was success {}", std::string(req.main_uuid_))); spdlog::info("Promote replica to main was success {}", std::string(req.main_uuid_));
slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder); slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder);
} }
@@ -184,5 +197,22 @@ void CoordinatorHandlers::UnregisterReplicaHandler(replication::ReplicationHandl
} }
} }
void CoordinatorHandlers::EnableWritingOnMainHandler(replication::ReplicationHandler &replication_handler,
slk::Reader * /*req_reader*/, slk::Builder *res_builder) {
if (!replication_handler.IsMain()) {
spdlog::error("Enable writing on main must be performed on main!");
slk::Save(coordination::EnableWritingOnMainRes{false}, res_builder);
return;
}
if (!replication_handler.GetReplState().EnableWritingOnMain()) {
spdlog::error("Enabling writing on main failed!");
slk::Save(coordination::EnableWritingOnMainRes{false}, res_builder);
return;
}
slk::Save(coordination::EnableWritingOnMainRes{true}, res_builder);
}
} // namespace memgraph::dbms } // namespace memgraph::dbms
#endif #endif

View File

@@ -14,9 +14,11 @@
#include "coordination/coordinator_instance.hpp" #include "coordination/coordinator_instance.hpp"
#include "coordination/coordinator_exceptions.hpp" #include "coordination/coordinator_exceptions.hpp"
#include "coordination/fmt.hpp"
#include "nuraft/coordinator_state_machine.hpp" #include "nuraft/coordinator_state_machine.hpp"
#include "nuraft/coordinator_state_manager.hpp" #include "nuraft/coordinator_state_manager.hpp"
#include "utils/counter.hpp" #include "utils/counter.hpp"
#include "utils/functional.hpp"
#include <range/v3/view.hpp> #include <range/v3/view.hpp>
#include <shared_mutex> #include <shared_mutex>
@@ -47,9 +49,12 @@ CoordinatorInstance::CoordinatorInstance()
spdlog::trace("Instance {} performing replica successful callback", repl_instance_name); spdlog::trace("Instance {} performing replica successful callback", repl_instance_name);
auto &repl_instance = find_repl_instance(self, repl_instance_name); auto &repl_instance = find_repl_instance(self, repl_instance_name);
// We need to get replicas UUID from time to time to ensure replica is listening to correct main
// and that it didn't go down for less time than we could notice
// We need to get id of main replica is listening to
// and swap if necessary
if (!repl_instance.EnsureReplicaHasCorrectMainUUID(self->GetMainUUID())) { if (!repl_instance.EnsureReplicaHasCorrectMainUUID(self->GetMainUUID())) {
spdlog::error( spdlog::error("Failed to swap uuid for replica instance {} which is alive", repl_instance.InstanceName());
fmt::format("Failed to swap uuid for replica instance {} which is alive", repl_instance.InstanceName()));
return; return;
} }
@@ -61,14 +66,6 @@ CoordinatorInstance::CoordinatorInstance()
spdlog::trace("Instance {} performing replica failure callback", repl_instance_name); spdlog::trace("Instance {} performing replica failure callback", repl_instance_name);
auto &repl_instance = find_repl_instance(self, repl_instance_name); auto &repl_instance = find_repl_instance(self, repl_instance_name);
repl_instance.OnFailPing(); repl_instance.OnFailPing();
// We need to restart main uuid from instance since it was "down" at least a second
// There is slight delay, if we choose to use isAlive, instance can be down and back up in less than
// our isAlive time difference, which would lead to instance setting UUID to nullopt and stopping accepting any
// incoming RPCs from valid main
// TODO(antoniofilipovic) this needs here more complex logic
// We need to get id of main replica is listening to on successful ping
// and swap it to correct uuid if it failed
repl_instance.ResetMainUUID();
}; };
main_succ_cb_ = [find_repl_instance](CoordinatorInstance *self, std::string_view repl_instance_name) -> void { main_succ_cb_ = [find_repl_instance](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
@@ -87,6 +84,11 @@ CoordinatorInstance::CoordinatorInstance()
auto const curr_main_uuid = self->GetMainUUID(); auto const curr_main_uuid = self->GetMainUUID();
if (curr_main_uuid == repl_instance_uuid.value()) { if (curr_main_uuid == repl_instance_uuid.value()) {
if (!repl_instance.EnableWritingOnMain()) {
spdlog::error("Failed to enable writing on main instance {}", repl_instance_name);
return;
}
repl_instance.OnSuccessPing(); repl_instance.OnSuccessPing();
return; return;
} }
@@ -125,9 +127,6 @@ CoordinatorInstance::CoordinatorInstance()
auto CoordinatorInstance::ShowInstances() const -> std::vector<InstanceStatus> { auto CoordinatorInstance::ShowInstances() const -> std::vector<InstanceStatus> {
auto const coord_instances = raft_state_.GetAllCoordinators(); auto const coord_instances = raft_state_.GetAllCoordinators();
std::vector<InstanceStatus> instances_status;
instances_status.reserve(repl_instances_.size() + coord_instances.size());
auto const stringify_repl_role = [](ReplicationInstance const &instance) -> std::string { auto const stringify_repl_role = [](ReplicationInstance const &instance) -> std::string {
if (!instance.IsAlive()) return "unknown"; if (!instance.IsAlive()) return "unknown";
if (instance.IsMain()) return "main"; if (instance.IsMain()) return "main";
@@ -149,8 +148,7 @@ auto CoordinatorInstance::ShowInstances() const -> std::vector<InstanceStatus> {
// CoordinatorState to every instance, we can be smarter about this using our RPC. // CoordinatorState to every instance, we can be smarter about this using our RPC.
}; };
std::ranges::transform(coord_instances, std::back_inserter(instances_status), coord_instance_to_status); auto instances_status = utils::fmap(coord_instance_to_status, coord_instances);
{ {
auto lock = std::shared_lock{coord_instance_lock_}; auto lock = std::shared_lock{coord_instance_lock_};
std::ranges::transform(repl_instances_, std::back_inserter(instances_status), repl_instance_to_status); std::ranges::transform(repl_instances_, std::back_inserter(instances_status), repl_instance_to_status);
@@ -189,10 +187,9 @@ auto CoordinatorInstance::TryFailover() -> void {
} }
} }
ReplicationClientsInfo repl_clients_info; auto repl_clients_info = repl_instances_ | ranges::views::filter(is_not_new_main) |
repl_clients_info.reserve(repl_instances_.size() - 1); ranges::views::transform(&ReplicationInstance::ReplicationClientInfo) |
std::ranges::transform(repl_instances_ | ranges::views::filter(is_not_new_main), ranges::to<ReplicationClientsInfo>();
std::back_inserter(repl_clients_info), &ReplicationInstance::ReplicationClientInfo);
if (!new_main->PromoteToMain(new_main_uuid, std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) { if (!new_main->PromoteToMain(new_main_uuid, std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) {
spdlog::warn("Failover failed since promoting replica to main failed!"); spdlog::warn("Failover failed since promoting replica to main failed!");
@@ -208,6 +205,10 @@ auto CoordinatorInstance::SetReplicationInstanceToMain(std::string instance_name
-> SetInstanceToMainCoordinatorStatus { -> SetInstanceToMainCoordinatorStatus {
auto lock = std::lock_guard{coord_instance_lock_}; auto lock = std::lock_guard{coord_instance_lock_};
if (std::ranges::any_of(repl_instances_, &ReplicationInstance::IsMain)) {
return SetInstanceToMainCoordinatorStatus::MAIN_ALREADY_EXISTS;
}
auto const is_new_main = [&instance_name](ReplicationInstance const &instance) { auto const is_new_main = [&instance_name](ReplicationInstance const &instance) {
return instance.InstanceName() == instance_name; return instance.InstanceName() == instance_name;
}; };

View File

@@ -68,6 +68,35 @@ void UnregisterReplicaRes::Load(UnregisterReplicaRes *self, memgraph::slk::Reade
memgraph::slk::Load(self, reader); memgraph::slk::Load(self, reader);
} }
void EnableWritingOnMainRes::Save(EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self, builder);
}
void EnableWritingOnMainRes::Load(EnableWritingOnMainRes *self, memgraph::slk::Reader *reader) {
memgraph::slk::Load(self, reader);
}
void EnableWritingOnMainReq::Save(EnableWritingOnMainReq const &self, memgraph::slk::Builder *builder) {}
void EnableWritingOnMainReq::Load(EnableWritingOnMainReq *self, memgraph::slk::Reader *reader) {}
// GetInstanceUUID
void GetInstanceUUIDReq::Save(const GetInstanceUUIDReq &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self, builder);
}
void GetInstanceUUIDReq::Load(GetInstanceUUIDReq *self, memgraph::slk::Reader *reader) {
memgraph::slk::Load(self, reader);
}
void GetInstanceUUIDRes::Save(const GetInstanceUUIDRes &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self, builder);
}
void GetInstanceUUIDRes::Load(GetInstanceUUIDRes *self, memgraph::slk::Reader *reader) {
memgraph::slk::Load(self, reader);
}
} // namespace coordination } // namespace coordination
constexpr utils::TypeInfo coordination::PromoteReplicaToMainReq::kType{utils::TypeId::COORD_FAILOVER_REQ, constexpr utils::TypeInfo coordination::PromoteReplicaToMainReq::kType{utils::TypeId::COORD_FAILOVER_REQ,
@@ -89,8 +118,22 @@ constexpr utils::TypeInfo coordination::UnregisterReplicaReq::kType{utils::TypeI
constexpr utils::TypeInfo coordination::UnregisterReplicaRes::kType{utils::TypeId::COORD_UNREGISTER_REPLICA_RES, constexpr utils::TypeInfo coordination::UnregisterReplicaRes::kType{utils::TypeId::COORD_UNREGISTER_REPLICA_RES,
"UnregisterReplicaRes", nullptr}; "UnregisterReplicaRes", nullptr};
constexpr utils::TypeInfo coordination::EnableWritingOnMainReq::kType{utils::TypeId::COORD_ENABLE_WRITING_ON_MAIN_REQ,
"CoordEnableWritingOnMainReq", nullptr};
constexpr utils::TypeInfo coordination::EnableWritingOnMainRes::kType{utils::TypeId::COORD_ENABLE_WRITING_ON_MAIN_RES,
"CoordEnableWritingOnMainRes", nullptr};
constexpr utils::TypeInfo coordination::GetInstanceUUIDReq::kType{utils::TypeId::COORD_GET_UUID_REQ, "CoordGetUUIDReq",
nullptr};
constexpr utils::TypeInfo coordination::GetInstanceUUIDRes::kType{utils::TypeId::COORD_GET_UUID_RES, "CoordGetUUIDRes",
nullptr};
namespace slk { namespace slk {
// PromoteReplicaToMainRpc
void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) { void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self.success, builder); memgraph::slk::Save(self.success, builder);
} }
@@ -109,6 +152,7 @@ void Load(memgraph::coordination::PromoteReplicaToMainReq *self, memgraph::slk::
memgraph::slk::Load(&self->replication_clients_info, reader); memgraph::slk::Load(&self->replication_clients_info, reader);
} }
// DemoteMainToReplicaRpc
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder) { void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self.replication_client_info, builder); memgraph::slk::Save(self.replication_client_info, builder);
} }
@@ -125,6 +169,8 @@ void Load(memgraph::coordination::DemoteMainToReplicaRes *self, memgraph::slk::R
memgraph::slk::Load(&self->success, reader); memgraph::slk::Load(&self->success, reader);
} }
// UnregisterReplicaRpc
void Save(memgraph::coordination::UnregisterReplicaReq const &self, memgraph::slk::Builder *builder) { void Save(memgraph::coordination::UnregisterReplicaReq const &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self.instance_name, builder); memgraph::slk::Save(self.instance_name, builder);
} }
@@ -141,6 +187,32 @@ void Load(memgraph::coordination::UnregisterReplicaRes *self, memgraph::slk::Rea
memgraph::slk::Load(&self->success, reader); memgraph::slk::Load(&self->success, reader);
} }
void Save(memgraph::coordination::EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self.success, builder);
}
void Load(memgraph::coordination::EnableWritingOnMainRes *self, memgraph::slk::Reader *reader) {
memgraph::slk::Load(&self->success, reader);
}
// GetInstanceUUIDRpc
void Save(const memgraph::coordination::GetInstanceUUIDReq & /*self*/, memgraph::slk::Builder * /*builder*/) {
/* nothing to serialize*/
}
void Load(memgraph::coordination::GetInstanceUUIDReq * /*self*/, memgraph::slk::Reader * /*reader*/) {
/* nothing to serialize*/
}
void Save(const memgraph::coordination::GetInstanceUUIDRes &self, memgraph::slk::Builder *builder) {
memgraph::slk::Save(self.uuid, builder);
}
void Load(memgraph::coordination::GetInstanceUUIDRes *self, memgraph::slk::Reader *reader) {
memgraph::slk::Load(&self->uuid, reader);
}
} // namespace slk } // namespace slk
} // namespace memgraph } // namespace memgraph

60
src/coordination/fmt.hpp Normal file
View File

@@ -0,0 +1,60 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#if FMT_VERSION > 90000
#include <fmt/ostream.h>
#include <string>
#include <libnuraft/nuraft.hxx>
#include "utils/logging.hpp"
inline std::string ToString(const nuraft::cmd_result_code &code) {
switch (code) {
case nuraft::cmd_result_code::OK:
return "OK";
case nuraft::cmd_result_code::FAILED:
return "FAILED";
case nuraft::cmd_result_code::RESULT_NOT_EXIST_YET:
return "RESULT_NOT_EXIST_YET";
case nuraft::cmd_result_code::TERM_MISMATCH:
return "TERM_MISMATCH";
case nuraft::cmd_result_code::SERVER_IS_LEAVING:
return "SERVER_IS_LEAVING";
case nuraft::cmd_result_code::CANNOT_REMOVE_LEADER:
return "CANNOT_REMOVE_LEADER";
case nuraft::cmd_result_code::SERVER_NOT_FOUND:
return "SERVER_NOT_FOUND";
case nuraft::cmd_result_code::SERVER_IS_JOINING:
return "SERVER_IS_JOINING";
case nuraft::cmd_result_code::CONFIG_CHANGING:
return "CONFIG_CHANGING";
case nuraft::cmd_result_code::SERVER_ALREADY_EXISTS:
return "SERVER_ALREADY_EXISTS";
case nuraft::cmd_result_code::BAD_REQUEST:
return "BAD_REQUEST";
case nuraft::cmd_result_code::NOT_LEADER:
return "NOT_LEADER";
case nuraft::cmd_result_code::TIMEOUT:
return "TIMEOUT";
case nuraft::cmd_result_code::CANCELLED:
return "CANCELLED";
}
LOG_FATAL("ToString of a nuraft::cmd_result_code -> check missing switch case");
}
inline std::ostream &operator<<(std::ostream &os, const nuraft::cmd_result_code &code) {
os << ToString(code);
return os;
}
template <>
class fmt::formatter<nuraft::cmd_result_code> : public fmt::ostream_formatter {};
#endif

View File

@@ -11,12 +11,14 @@
#pragma once #pragma once
#include "utils/uuid.hpp"
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
#include "coordination/coordinator_config.hpp" #include "coordination/coordinator_config.hpp"
#include "rpc/client.hpp" #include "rpc/client.hpp"
#include "rpc_errors.hpp"
#include "utils/result.hpp"
#include "utils/scheduler.hpp" #include "utils/scheduler.hpp"
#include "utils/uuid.hpp"
namespace memgraph::coordination { namespace memgraph::coordination {
@@ -46,7 +48,7 @@ class CoordinatorClient {
auto SocketAddress() const -> std::string; auto SocketAddress() const -> std::string;
[[nodiscard]] auto DemoteToReplica() const -> bool; [[nodiscard]] auto DemoteToReplica() const -> bool;
// TODO: (andi) Consistent naming
auto SendPromoteReplicaToMainRpc(const utils::UUID &uuid, ReplicationClientsInfo replication_clients_info) const auto SendPromoteReplicaToMainRpc(const utils::UUID &uuid, ReplicationClientsInfo replication_clients_info) const
-> bool; -> bool;
@@ -54,6 +56,10 @@ class CoordinatorClient {
auto SendUnregisterReplicaRpc(std::string const &instance_name) const -> bool; auto SendUnregisterReplicaRpc(std::string const &instance_name) const -> bool;
auto SendEnableWritingOnMainRpc() const -> bool;
auto SendGetInstanceUUIDRpc() const -> memgraph::utils::BasicResult<GetInstanceUUIDError, std::optional<utils::UUID>>;
auto ReplicationClientInfo() const -> ReplClientInfo; auto ReplicationClientInfo() const -> ReplClientInfo;
auto SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void; auto SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void;
@@ -62,6 +68,8 @@ class CoordinatorClient {
auto InstanceDownTimeoutSec() const -> std::chrono::seconds; auto InstanceDownTimeoutSec() const -> std::chrono::seconds;
auto InstanceGetUUIDFrequencySec() const -> std::chrono::seconds;
friend bool operator==(CoordinatorClient const &first, CoordinatorClient const &second) { friend bool operator==(CoordinatorClient const &first, CoordinatorClient const &second) {
return first.config_ == second.config_; return first.config_ == second.config_;
} }
@@ -69,7 +77,6 @@ class CoordinatorClient {
private: private:
utils::Scheduler instance_checker_; utils::Scheduler instance_checker_;
// TODO: (andi) Pimpl?
communication::ClientContext rpc_context_; communication::ClientContext rpc_context_;
mutable rpc::Client rpc_client_; mutable rpc::Client rpc_client_;

View File

@@ -30,6 +30,7 @@ struct CoordinatorClientConfig {
uint16_t port{}; uint16_t port{};
std::chrono::seconds instance_health_check_frequency_sec{1}; std::chrono::seconds instance_health_check_frequency_sec{1};
std::chrono::seconds instance_down_timeout_sec{5}; std::chrono::seconds instance_down_timeout_sec{5};
std::chrono::seconds instance_get_uuid_frequency_sec{10};
auto SocketAddress() const -> std::string { return ip_address + ":" + std::to_string(port); } auto SocketAddress() const -> std::string { return ip_address + ":" + std::to_string(port); }

View File

@@ -36,6 +36,11 @@ class CoordinatorHandlers {
static void UnregisterReplicaHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader, static void UnregisterReplicaHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
slk::Builder *res_builder); slk::Builder *res_builder);
static void EnableWritingOnMainHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
slk::Builder *res_builder);
static void GetInstanceUUIDHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
slk::Builder *res_builder);
}; };
} // namespace memgraph::dbms } // namespace memgraph::dbms

View File

@@ -111,6 +111,56 @@ struct UnregisterReplicaRes {
using UnregisterReplicaRpc = rpc::RequestResponse<UnregisterReplicaReq, UnregisterReplicaRes>; using UnregisterReplicaRpc = rpc::RequestResponse<UnregisterReplicaReq, UnregisterReplicaRes>;
struct EnableWritingOnMainReq {
static const utils::TypeInfo kType;
static const utils::TypeInfo &GetTypeInfo() { return kType; }
static void Load(EnableWritingOnMainReq *self, memgraph::slk::Reader *reader);
static void Save(EnableWritingOnMainReq const &self, memgraph::slk::Builder *builder);
EnableWritingOnMainReq() = default;
};
struct EnableWritingOnMainRes {
static const utils::TypeInfo kType;
static const utils::TypeInfo &GetTypeInfo() { return kType; }
static void Load(EnableWritingOnMainRes *self, memgraph::slk::Reader *reader);
static void Save(EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder);
explicit EnableWritingOnMainRes(bool success) : success(success) {}
EnableWritingOnMainRes() = default;
bool success;
};
using EnableWritingOnMainRpc = rpc::RequestResponse<EnableWritingOnMainReq, EnableWritingOnMainRes>;
struct GetInstanceUUIDReq {
static const utils::TypeInfo kType;
static const utils::TypeInfo &GetTypeInfo() { return kType; }
static void Load(GetInstanceUUIDReq *self, memgraph::slk::Reader *reader);
static void Save(const GetInstanceUUIDReq &self, memgraph::slk::Builder *builder);
GetInstanceUUIDReq() = default;
};
struct GetInstanceUUIDRes {
static const utils::TypeInfo kType;
static const utils::TypeInfo &GetTypeInfo() { return kType; }
static void Load(GetInstanceUUIDRes *self, memgraph::slk::Reader *reader);
static void Save(const GetInstanceUUIDRes &self, memgraph::slk::Builder *builder);
explicit GetInstanceUUIDRes(std::optional<utils::UUID> uuid) : uuid(uuid) {}
GetInstanceUUIDRes() = default;
std::optional<utils::UUID> uuid;
};
using GetInstanceUUIDRpc = rpc::RequestResponse<GetInstanceUUIDReq, GetInstanceUUIDRes>;
} // namespace memgraph::coordination } // namespace memgraph::coordination
// SLK serialization declarations // SLK serialization declarations
@@ -128,12 +178,20 @@ void Load(memgraph::coordination::DemoteMainToReplicaRes *self, memgraph::slk::R
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder); void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder);
void Load(memgraph::coordination::DemoteMainToReplicaReq *self, memgraph::slk::Reader *reader); void Load(memgraph::coordination::DemoteMainToReplicaReq *self, memgraph::slk::Reader *reader);
// GetInstanceUUIDRpc
void Save(const memgraph::coordination::GetInstanceUUIDReq &self, memgraph::slk::Builder *builder);
void Load(memgraph::coordination::GetInstanceUUIDReq *self, memgraph::slk::Reader *reader);
void Save(const memgraph::coordination::GetInstanceUUIDRes &self, memgraph::slk::Builder *builder);
void Load(memgraph::coordination::GetInstanceUUIDRes *self, memgraph::slk::Reader *reader);
// UnregisterReplicaRpc // UnregisterReplicaRpc
void Save(memgraph::coordination::UnregisterReplicaRes const &self, memgraph::slk::Builder *builder); void Save(memgraph::coordination::UnregisterReplicaRes const &self, memgraph::slk::Builder *builder);
void Load(memgraph::coordination::UnregisterReplicaRes *self, memgraph::slk::Reader *reader); void Load(memgraph::coordination::UnregisterReplicaRes *self, memgraph::slk::Reader *reader);
void Save(memgraph::coordination::UnregisterReplicaReq const &self, memgraph::slk::Builder *builder); void Save(memgraph::coordination::UnregisterReplicaReq const &self, memgraph::slk::Builder *builder);
void Load(memgraph::coordination::UnregisterReplicaReq *self, memgraph::slk::Reader *reader); void Load(memgraph::coordination::UnregisterReplicaReq *self, memgraph::slk::Reader *reader);
void Save(memgraph::coordination::EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder);
void Load(memgraph::coordination::EnableWritingOnMainRes *self, memgraph::slk::Reader *reader);
} // namespace memgraph::slk } // namespace memgraph::slk
#endif #endif

View File

@@ -15,8 +15,6 @@
#include <flags/replication.hpp> #include <flags/replication.hpp>
#include <optional>
#include <libnuraft/nuraft.hxx> #include <libnuraft/nuraft.hxx>
namespace memgraph::coordination { namespace memgraph::coordination {

View File

@@ -39,6 +39,7 @@ enum class UnregisterInstanceCoordinatorStatus : uint8_t {
enum class SetInstanceToMainCoordinatorStatus : uint8_t { enum class SetInstanceToMainCoordinatorStatus : uint8_t {
NO_INSTANCE_WITH_NAME, NO_INSTANCE_WITH_NAME,
MAIN_ALREADY_EXISTS,
NOT_COORDINATOR, NOT_COORDINATOR,
SUCCESS, SUCCESS,
COULD_NOT_PROMOTE_TO_MAIN, COULD_NOT_PROMOTE_TO_MAIN,

View File

@@ -18,6 +18,7 @@
#include "replication_coordination_glue/role.hpp" #include "replication_coordination_glue/role.hpp"
#include <libnuraft/nuraft.hxx> #include <libnuraft/nuraft.hxx>
#include "utils/result.hpp"
#include "utils/uuid.hpp" #include "utils/uuid.hpp"
namespace memgraph::coordination { namespace memgraph::coordination {
@@ -37,6 +38,9 @@ class ReplicationInstance {
auto OnSuccessPing() -> void; auto OnSuccessPing() -> void;
auto OnFailPing() -> bool; auto OnFailPing() -> bool;
auto IsReadyForUUIDPing() -> bool;
void UpdateReplicaLastResponseUUID();
auto IsAlive() const -> bool; auto IsAlive() const -> bool;
@@ -62,9 +66,12 @@ class ReplicationInstance {
auto SendSwapAndUpdateUUID(const utils::UUID &new_main_uuid) -> bool; auto SendSwapAndUpdateUUID(const utils::UUID &new_main_uuid) -> bool;
auto SendUnregisterReplicaRpc(std::string const &instance_name) -> bool; auto SendUnregisterReplicaRpc(std::string const &instance_name) -> bool;
// TODO: (andi) Inconsistent API
auto SendGetInstanceUUID() -> utils::BasicResult<coordination::GetInstanceUUIDError, std::optional<utils::UUID>>;
auto GetClient() -> CoordinatorClient &; auto GetClient() -> CoordinatorClient &;
auto EnableWritingOnMain() -> bool;
auto SetNewMainUUID(utils::UUID const &main_uuid) -> void; auto SetNewMainUUID(utils::UUID const &main_uuid) -> void;
auto ResetMainUUID() -> void; auto ResetMainUUID() -> void;
auto GetMainUUID() const -> const std::optional<utils::UUID> &; auto GetMainUUID() const -> const std::optional<utils::UUID> &;
@@ -74,6 +81,7 @@ class ReplicationInstance {
replication_coordination_glue::ReplicationRole replication_role_; replication_coordination_glue::ReplicationRole replication_role_;
std::chrono::system_clock::time_point last_response_time_{}; std::chrono::system_clock::time_point last_response_time_{};
bool is_alive_{false}; bool is_alive_{false};
std::chrono::system_clock::time_point last_check_of_uuid_{};
// for replica this is main uuid of current main // for replica this is main uuid of current main
// for "main" main this same as in CoordinatorData // for "main" main this same as in CoordinatorData

View File

@@ -9,14 +9,6 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#pragma once
namespace memgraph::coordination { namespace memgraph::coordination {
enum class GetInstanceUUIDError { NO_RESPONSE, RPC_EXCEPTION };
#ifdef MG_EXPERIMENTAL_HIGH_AVAILABILITY
constexpr bool allow_ha = true;
#else
constexpr bool allow_ha = false;
#endif
} // namespace memgraph::coordination } // namespace memgraph::coordination

View File

@@ -14,6 +14,7 @@
#include "coordination/replication_instance.hpp" #include "coordination/replication_instance.hpp"
#include "replication_coordination_glue/handler.hpp" #include "replication_coordination_glue/handler.hpp"
#include "utils/result.hpp"
namespace memgraph::coordination { namespace memgraph::coordination {
@@ -39,6 +40,11 @@ auto ReplicationInstance::OnFailPing() -> bool {
return is_alive_; return is_alive_;
} }
auto ReplicationInstance::IsReadyForUUIDPing() -> bool {
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - last_check_of_uuid_) >
client_.InstanceGetUUIDFrequencySec();
}
auto ReplicationInstance::InstanceName() const -> std::string { return client_.InstanceName(); } auto ReplicationInstance::InstanceName() const -> std::string { return client_.InstanceName(); }
auto ReplicationInstance::SocketAddress() const -> std::string { return client_.SocketAddress(); } auto ReplicationInstance::SocketAddress() const -> std::string { return client_.SocketAddress(); }
auto ReplicationInstance::IsAlive() const -> bool { return is_alive_; } auto ReplicationInstance::IsAlive() const -> bool { return is_alive_; }
@@ -91,10 +97,20 @@ auto ReplicationInstance::ResetMainUUID() -> void { main_uuid_ = std::nullopt; }
auto ReplicationInstance::GetMainUUID() const -> std::optional<utils::UUID> const & { return main_uuid_; } auto ReplicationInstance::GetMainUUID() const -> std::optional<utils::UUID> const & { return main_uuid_; }
auto ReplicationInstance::EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool { auto ReplicationInstance::EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool {
if (!main_uuid_ || *main_uuid_ != curr_main_uuid) { if (!IsReadyForUUIDPing()) {
return SendSwapAndUpdateUUID(curr_main_uuid); return true;
} }
return true; auto res = SendGetInstanceUUID();
if (res.HasError()) {
return false;
}
UpdateReplicaLastResponseUUID();
if (res.GetValue().has_value() && res.GetValue().value() == curr_main_uuid) {
return true;
}
return SendSwapAndUpdateUUID(curr_main_uuid);
} }
auto ReplicationInstance::SendSwapAndUpdateUUID(const utils::UUID &new_main_uuid) -> bool { auto ReplicationInstance::SendSwapAndUpdateUUID(const utils::UUID &new_main_uuid) -> bool {
@@ -109,5 +125,14 @@ auto ReplicationInstance::SendUnregisterReplicaRpc(std::string const &instance_n
return client_.SendUnregisterReplicaRpc(instance_name); return client_.SendUnregisterReplicaRpc(instance_name);
} }
auto ReplicationInstance::EnableWritingOnMain() -> bool { return client_.SendEnableWritingOnMainRpc(); }
auto ReplicationInstance::SendGetInstanceUUID()
-> utils::BasicResult<coordination::GetInstanceUUIDError, std::optional<utils::UUID>> {
return client_.SendGetInstanceUUIDRpc();
}
void ReplicationInstance::UpdateReplicaLastResponseUUID() { last_check_of_uuid_ = std::chrono::system_clock::now(); }
} // namespace memgraph::coordination } // namespace memgraph::coordination
#endif #endif

View File

@@ -110,9 +110,9 @@ class Database {
* @param force_directory Use the configured directory, do not try to decipher the multi-db version * @param force_directory Use the configured directory, do not try to decipher the multi-db version
* @return DatabaseInfo * @return DatabaseInfo
*/ */
DatabaseInfo GetInfo(bool force_directory, replication_coordination_glue::ReplicationRole replication_role) const { DatabaseInfo GetInfo(replication_coordination_glue::ReplicationRole replication_role) const {
DatabaseInfo info; DatabaseInfo info;
info.storage_info = storage_->GetInfo(force_directory, replication_role); info.storage_info = storage_->GetInfo(replication_role);
info.triggers = trigger_store_.GetTriggerInfo().size(); info.triggers = trigger_store_.GetTriggerInfo().size();
info.streams = streams_.GetStreamInfo().size(); info.streams = streams_.GetStreamInfo().size();
return info; return info;

View File

@@ -302,7 +302,7 @@ class DbmsHandler {
auto db_acc_opt = db_gk.access(); auto db_acc_opt = db_gk.access();
if (db_acc_opt) { if (db_acc_opt) {
auto &db_acc = *db_acc_opt; auto &db_acc = *db_acc_opt;
const auto &info = db_acc->GetInfo(false, replication_role); const auto &info = db_acc->GetInfo(replication_role);
const auto &storage_info = info.storage_info; const auto &storage_info = info.storage_info;
stats.num_vertex += storage_info.vertex_count; stats.num_vertex += storage_info.vertex_count;
stats.num_edges += storage_info.edge_count; stats.num_edges += storage_info.edge_count;
@@ -338,7 +338,7 @@ class DbmsHandler {
auto db_acc_opt = db_gk.access(); auto db_acc_opt = db_gk.access();
if (db_acc_opt) { if (db_acc_opt) {
auto &db_acc = *db_acc_opt; auto &db_acc = *db_acc_opt;
res.push_back(db_acc->GetInfo(false, replication_role)); res.push_back(db_acc->GetInfo(replication_role));
} }
} }
return res; return res;

View File

@@ -19,6 +19,7 @@
#include "storage/v2/durability/durability.hpp" #include "storage/v2/durability/durability.hpp"
#include "storage/v2/durability/snapshot.hpp" #include "storage/v2/durability/snapshot.hpp"
#include "storage/v2/durability/version.hpp" #include "storage/v2/durability/version.hpp"
#include "storage/v2/fmt.hpp"
#include "storage/v2/indices/label_index_stats.hpp" #include "storage/v2/indices/label_index_stats.hpp"
#include "storage/v2/inmemory/storage.hpp" #include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/inmemory/unique_constraints.hpp" #include "storage/v2/inmemory/unique_constraints.hpp"

View File

View File

@@ -10,6 +10,7 @@
// licenses/APL.txt. // licenses/APL.txt.
#pragma once #pragma once
#include <algorithm>
#include <atomic> #include <atomic>
#include <compare> #include <compare>
#include <cstdint> #include <cstdint>

View File

@@ -19,13 +19,14 @@
// Bolt server flags. // Bolt server flags.
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(experimental_enabled, "", DEFINE_string(experimental_enabled, "",
"Experimental features to be used, comma seperated. Options [system-replication]"); "Experimental features to be used, comma seperated. Options [system-replication, high-availability]");
using namespace std::string_view_literals; using namespace std::string_view_literals;
namespace memgraph::flags { namespace memgraph::flags {
auto const mapping = std::map{std::pair{"system-replication"sv, Experiments::SYSTEM_REPLICATION}}; auto const mapping = std::map{std::pair{"system-replication"sv, Experiments::SYSTEM_REPLICATION},
std::pair{"high-availability"sv, Experiments::HIGH_AVAILABILITY}};
auto ExperimentsInstance() -> Experiments & { auto ExperimentsInstance() -> Experiments & {
static auto instance = Experiments{}; static auto instance = Experiments{};

View File

@@ -23,6 +23,7 @@ namespace memgraph::flags {
// old experiments can be reused once code cleanup has happened // old experiments can be reused once code cleanup has happened
enum class Experiments : uint8_t { enum class Experiments : uint8_t {
SYSTEM_REPLICATION = 1 << 0, SYSTEM_REPLICATION = 1 << 0,
HIGH_AVAILABILITY = 1 << 1,
}; };
bool AreExperimentsEnabled(Experiments experiments); bool AreExperimentsEnabled(Experiments experiments);

View File

@@ -22,6 +22,8 @@ DEFINE_uint32(raft_server_id, 0, "Unique ID of the raft server.");
DEFINE_uint32(instance_down_timeout_sec, 5, "Time duration after which an instance is considered down."); DEFINE_uint32(instance_down_timeout_sec, 5, "Time duration after which an instance is considered down.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_uint32(instance_health_check_frequency_sec, 1, "The time duration between two health checks/pings."); DEFINE_uint32(instance_health_check_frequency_sec, 1, "The time duration between two health checks/pings.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_uint32(instance_get_uuid_frequency_sec, 10, "The time duration between two instance uuid checks.");
#endif #endif
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -24,6 +24,8 @@ DECLARE_uint32(raft_server_id);
DECLARE_uint32(instance_down_timeout_sec); DECLARE_uint32(instance_down_timeout_sec);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_uint32(instance_health_check_frequency_sec); DECLARE_uint32(instance_health_check_frequency_sec);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_uint32(instance_get_uuid_frequency_sec);
#endif #endif
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -6,5 +6,6 @@ target_sources(mg-glue PRIVATE auth.cpp
SessionHL.cpp SessionHL.cpp
ServerT.cpp ServerT.cpp
MonitoringServerT.cpp MonitoringServerT.cpp
run_id.cpp) run_id.cpp
query_user.cpp)
target_link_libraries(mg-glue mg-query mg-auth mg-audit mg-flags) target_link_libraries(mg-glue mg-query mg-auth mg-audit mg-flags)

View File

@@ -11,6 +11,7 @@
#include <optional> #include <optional>
#include <utility> #include <utility>
#include "auth/auth.hpp"
#include "gflags/gflags.h" #include "gflags/gflags.h"
#include "audit/log.hpp" #include "audit/log.hpp"
@@ -19,17 +20,22 @@
#include "glue/SessionHL.hpp" #include "glue/SessionHL.hpp"
#include "glue/auth_checker.hpp" #include "glue/auth_checker.hpp"
#include "glue/communication.hpp" #include "glue/communication.hpp"
#include "glue/query_user.hpp"
#include "glue/run_id.hpp" #include "glue/run_id.hpp"
#include "license/license.hpp" #include "license/license.hpp"
#include "query/auth_checker.hpp"
#include "query/discard_value_stream.hpp" #include "query/discard_value_stream.hpp"
#include "query/interpreter_context.hpp" #include "query/interpreter_context.hpp"
#include "query/query_user.hpp"
#include "utils/event_map.hpp" #include "utils/event_map.hpp"
#include "utils/spin_lock.hpp" #include "utils/spin_lock.hpp"
#include "utils/variant_helpers.hpp"
namespace memgraph::metrics { namespace memgraph::metrics {
extern const Event ActiveBoltSessions; extern const Event ActiveBoltSessions;
} // namespace memgraph::metrics } // namespace memgraph::metrics
namespace {
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras { auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
auto const &as_map = extra.ValueMap(); auto const &as_map = extra.ValueMap();
@@ -97,20 +103,24 @@ std::vector<memgraph::communication::bolt::Value> TypedValueResultStreamBase::De
} }
return decoded_values; return decoded_values;
} }
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::storage::Storage *storage) : storage_(storage) {} TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::storage::Storage *storage) : storage_(storage) {}
namespace memgraph::glue {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
inline static void MultiDatabaseAuth(const std::optional<auth::User> &user, std::string_view db) { void MultiDatabaseAuth(memgraph::query::QueryUserOrRole *user, std::string_view db) {
if (user && !AuthChecker::IsUserAuthorized(*user, {}, std::string(db))) { if (user && !user->IsAuthorized({}, std::string(db), &memgraph::query::session_long_policy)) {
throw memgraph::communication::bolt::ClientError( throw memgraph::communication::bolt::ClientError(
"You are not authorized on the database \"{}\"! Please contact your database administrator.", db); "You are not authorized on the database \"{}\"! Please contact your database administrator.", db);
} }
} }
#endif
} // namespace
namespace memgraph::glue {
#ifdef MG_ENTERPRISE
std::string SessionHL::GetDefaultDB() { std::string SessionHL::GetDefaultDB() {
if (user_.has_value()) { if (user_or_role_) {
return user_->db_access().GetDefault(); return user_or_role_->GetDefaultDB();
} }
return std::string{memgraph::dbms::kDefaultDB}; return std::string{memgraph::dbms::kDefaultDB};
} }
@@ -132,13 +142,18 @@ bool SessionHL::Authenticate(const std::string &username, const std::string &pas
interpreter_.ResetUser(); interpreter_.ResetUser();
{ {
auto locked_auth = auth_->Lock(); auto locked_auth = auth_->Lock();
if (locked_auth->HasUsers()) { if (locked_auth->AccessControlled()) {
user_ = locked_auth->Authenticate(username, password); const auto user_or_role = locked_auth->Authenticate(username, password);
if (user_.has_value()) { if (user_or_role.has_value()) {
interpreter_.SetUser(user_->username()); user_or_role_ = AuthChecker::GenQueryUser(auth_, *user_or_role);
interpreter_.SetUser(AuthChecker::GenQueryUser(auth_, *user_or_role));
} else { } else {
res = false; res = false;
} }
} else {
// No access control -> give empty user
user_or_role_ = AuthChecker::GenQueryUser(auth_, std::nullopt);
interpreter_.SetUser(AuthChecker::GenQueryUser(auth_, std::nullopt));
} }
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
@@ -195,21 +210,17 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
const std::string *username{nullptr};
if (user_) {
username = &user_->username();
}
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
auto &db = interpreter_.current_db_.db_acc_; auto &db = interpreter_.current_db_.db_acc_;
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query, const auto username = user_or_role_ ? (user_or_role_->username() ? *user_or_role_->username() : "") : "";
memgraph::storage::PropertyValue(params_pv), db ? db->get()->name() : "no known database"); audit_log_->Record(endpoint_.address().to_string(), username, query, memgraph::storage::PropertyValue(params_pv),
db ? db->get()->name() : "no known database");
} }
#endif #endif
try { try {
auto result = interpreter_.Prepare(query, params_pv, ToQueryExtras(extra)); auto result = interpreter_.Prepare(query, params_pv, ToQueryExtras(extra));
const std::string db_name = result.db ? *result.db : ""; const std::string db_name = result.db ? *result.db : "";
if (user_ && !AuthChecker::IsUserAuthorized(*user_, result.privileges, db_name)) { if (user_or_role_ && !user_or_role_->IsAuthorized(result.privileges, db_name, &query::session_long_policy)) {
interpreter_.Abort(); interpreter_.Abort();
if (db_name.empty()) { if (db_name.empty()) {
throw memgraph::communication::bolt::ClientError( throw memgraph::communication::bolt::ClientError(
@@ -311,7 +322,7 @@ void SessionHL::Configure(const std::map<std::string, memgraph::communication::b
// Check if the underlying database needs to be updated // Check if the underlying database needs to be updated
if (update) { if (update) {
MultiDatabaseAuth(user_, db); MultiDatabaseAuth(user_or_role_.get(), db);
interpreter_.SetCurrentDB(db, in_explicit_db_); interpreter_.SetCurrentDB(db, in_explicit_db_);
} }
#endif #endif
@@ -338,7 +349,7 @@ SessionHL::SessionHL(memgraph::query::InterpreterContext *interpreter_context,
// Metrics update // Metrics update
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions); memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
interpreter_.OnChangeCB([&](std::string_view db_name) { MultiDatabaseAuth(user_, db_name); }); interpreter_.OnChangeCB([&](std::string_view db_name) { MultiDatabaseAuth(user_or_role_.get(), db_name); });
#endif #endif
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); }); interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); });
} }

View File

@@ -15,6 +15,7 @@
#include "communication/v2/server.hpp" #include "communication/v2/server.hpp"
#include "communication/v2/session.hpp" #include "communication/v2/session.hpp"
#include "dbms/database.hpp" #include "dbms/database.hpp"
#include "glue/query_user.hpp"
#include "query/interpreter.hpp" #include "query/interpreter.hpp"
namespace memgraph::glue { namespace memgraph::glue {
@@ -82,7 +83,7 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
memgraph::query::InterpreterContext *interpreter_context_; memgraph::query::InterpreterContext *interpreter_context_;
memgraph::query::Interpreter interpreter_; memgraph::query::Interpreter interpreter_;
std::optional<memgraph::auth::User> user_; std::unique_ptr<query::QueryUserOrRole> user_or_role_;
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
memgraph::audit::Log *audit_log_; memgraph::audit::Log *audit_log_;
bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata

View File

@@ -14,53 +14,74 @@
#include "auth/auth.hpp" #include "auth/auth.hpp"
#include "auth/models.hpp" #include "auth/models.hpp"
#include "glue/auth.hpp" #include "glue/auth.hpp"
#include "glue/query_user.hpp"
#include "license/license.hpp" #include "license/license.hpp"
#include "query/auth_checker.hpp"
#include "query/constants.hpp" #include "query/constants.hpp"
#include "query/frontend/ast/ast.hpp" #include "query/frontend/ast/ast.hpp"
#include "query/query_user.hpp"
#include "utils/logging.hpp"
#include "utils/synchronized.hpp" #include "utils/synchronized.hpp"
#include "utils/variant_helpers.hpp"
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
namespace { namespace {
bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba, bool IsAuthorizedLabels(const memgraph::auth::UserOrRole &user_or_role, const memgraph::query::DbAccessor *dba,
const std::vector<memgraph::storage::LabelId> &labels, const std::vector<memgraph::storage::LabelId> &labels,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) { const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return true; return true;
} }
return std::all_of(labels.begin(), labels.end(), [dba, &user, fine_grained_privilege](const auto &label) { return std::all_of(labels.begin(), labels.end(), [dba, &user_or_role, fine_grained_privilege](const auto &label) {
return user.GetFineGrainedAccessLabelPermissions().Has( return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
dba->LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission( return user_or_role.GetFineGrainedAccessLabelPermissions().Has(
fine_grained_privilege)) == memgraph::auth::PermissionLevel::GRANT; dba->LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
fine_grained_privilege)) ==
memgraph::auth::PermissionLevel::GRANT;
}},
user_or_role);
}); });
} }
bool IsUserAuthorizedGloballyLabels(const memgraph::auth::User &user, bool IsAuthorizedGloballyLabels(const memgraph::auth::UserOrRole &user_or_role,
const memgraph::auth::FineGrainedPermission fine_grained_permission) { const memgraph::auth::FineGrainedPermission fine_grained_permission) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return true; return true;
} }
return user.GetFineGrainedAccessLabelPermissions().Has(memgraph::query::kAsterisk, fine_grained_permission) == return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
memgraph::auth::PermissionLevel::GRANT; return user_or_role.GetFineGrainedAccessLabelPermissions().Has(memgraph::query::kAsterisk,
fine_grained_permission) ==
memgraph::auth::PermissionLevel::GRANT;
}},
user_or_role);
} }
bool IsUserAuthorizedGloballyEdges(const memgraph::auth::User &user, bool IsAuthorizedGloballyEdges(const memgraph::auth::UserOrRole &user_or_role,
const memgraph::auth::FineGrainedPermission fine_grained_permission) { const memgraph::auth::FineGrainedPermission fine_grained_permission) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return true; return true;
} }
return user.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::query::kAsterisk, fine_grained_permission) == return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
memgraph::auth::PermissionLevel::GRANT; return user_or_role.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::query::kAsterisk,
fine_grained_permission) ==
memgraph::auth::PermissionLevel::GRANT;
}},
user_or_role);
} }
bool IsUserAuthorizedEdgeType(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba, bool IsAuthorizedEdgeType(const memgraph::auth::UserOrRole &user_or_role, const memgraph::query::DbAccessor *dba,
const memgraph::storage::EdgeTypeId &edgeType, const memgraph::storage::EdgeTypeId &edgeType,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) { const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return true; return true;
} }
return user.GetFineGrainedAccessEdgeTypePermissions().Has( return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
dba->EdgeTypeToName(edgeType), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission( return user_or_role.GetFineGrainedAccessEdgeTypePermissions().Has(
fine_grained_privilege)) == memgraph::auth::PermissionLevel::GRANT; dba->EdgeTypeToName(edgeType),
memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege)) ==
memgraph::auth::PermissionLevel::GRANT;
}},
user_or_role);
} }
} // namespace } // namespace
#endif #endif
@@ -68,47 +89,54 @@ namespace memgraph::glue {
AuthChecker::AuthChecker(memgraph::auth::SynchedAuth *auth) : auth_(auth) {} AuthChecker::AuthChecker(memgraph::auth::SynchedAuth *auth) : auth_(auth) {}
bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username, std::shared_ptr<query::QueryUserOrRole> AuthChecker::GenQueryUser(const std::optional<std::string> &username,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges, const std::optional<std::string> &rolename) const {
const std::string &db_name) const { const auto user_or_role = auth_->ReadLock()->GetUserOrRole(username, rolename);
std::optional<memgraph::auth::User> maybe_user; if (user_or_role) {
{ return std::make_shared<QueryUserOrRole>(auth_, *user_or_role);
auto locked_auth = auth_->ReadLock();
if (!locked_auth->HasUsers()) {
return true;
}
if (username.has_value()) {
maybe_user = locked_auth->GetUser(*username);
}
} }
// No user or role
return std::make_shared<QueryUserOrRole>(auth_);
}
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges, db_name); std::unique_ptr<query::QueryUserOrRole> AuthChecker::GenQueryUser(auth::SynchedAuth *auth,
const std::optional<auth::UserOrRole> &user_or_role) {
if (user_or_role) {
return std::visit(
utils::Overloaded{[&](auto &user_or_role) { return std::make_unique<QueryUserOrRole>(auth, user_or_role); }},
*user_or_role);
}
// No user or role
return std::make_unique<QueryUserOrRole>(auth);
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGrainedAuthChecker( std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGrainedAuthChecker(
const std::string &username, const memgraph::query::DbAccessor *dba) const { std::shared_ptr<query::QueryUserOrRole> user_or_role, const memgraph::query::DbAccessor *dba) const {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return {}; return {};
} }
try { if (!user_or_role || !*user_or_role) {
auto user = user_.Lock(); throw query::QueryRuntimeException("No user specified for fine grained authorization!");
if (username != user->username()) {
auto maybe_user = auth_->ReadLock()->GetUser(username);
if (!maybe_user) {
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
}
*user = std::move(*maybe_user);
}
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(*user, dba);
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
} }
}
void AuthChecker::ClearCache() const { // Convert from query user to auth user or role
user_.WithLock([](auto &user) mutable { user = {}; }); try {
auto glue_user = dynamic_cast<glue::QueryUserOrRole &>(*user_or_role);
if (glue_user.user_) {
return std::make_unique<glue::FineGrainedAuthChecker>(std::move(*glue_user.user_), dba);
}
if (glue_user.role_) {
return std::make_unique<glue::FineGrainedAuthChecker>(
auth::RoleWUsername{*glue_user.username(), std::move(*glue_user.role_)}, dba);
}
DMG_ASSERT(false, "Glue user has neither user not role");
} catch (std::bad_cast &e) {
DMG_ASSERT(false, "Using a non-glue user in glue...");
}
// Should never get here
return {};
} }
#endif #endif
@@ -116,7 +144,7 @@ bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges, const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
const std::string &db_name) { // NOLINT const std::string &db_name) { // NOLINT
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
if (!db_name.empty() && !user.db_access().Contains(db_name)) { if (!db_name.empty() && !user.HasAccess(db_name)) {
return false; return false;
} }
#endif #endif
@@ -127,9 +155,34 @@ bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
}); });
} }
bool AuthChecker::IsRoleAuthorized(const memgraph::auth::Role &role,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
const std::string &db_name) { // NOLINT
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
FineGrainedAuthChecker::FineGrainedAuthChecker(auth::User user, const memgraph::query::DbAccessor *dba) if (!db_name.empty() && !role.HasAccess(db_name)) {
: user_{std::move(user)}, dba_(dba){}; return false;
}
#endif
const auto role_permissions = role.permissions();
return std::all_of(privileges.begin(), privileges.end(), [&role_permissions](const auto privilege) {
return role_permissions.Has(memgraph::glue::PrivilegeToPermission(privilege)) ==
memgraph::auth::PermissionLevel::GRANT;
});
}
bool AuthChecker::IsUserOrRoleAuthorized(const memgraph::auth::UserOrRole &user_or_role,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
const std::string &db_name) {
return std::visit(
utils::Overloaded{
[&](const auth::User &user) -> bool { return AuthChecker::IsUserAuthorized(user, privileges, db_name); },
[&](const auth::Role &role) -> bool { return AuthChecker::IsRoleAuthorized(role, privileges, db_name); }},
user_or_role);
}
#ifdef MG_ENTERPRISE
FineGrainedAuthChecker::FineGrainedAuthChecker(auth::UserOrRole user_or_role, const memgraph::query::DbAccessor *dba)
: user_or_role_{std::move(user_or_role)}, dba_(dba){};
bool FineGrainedAuthChecker::Has(const memgraph::query::VertexAccessor &vertex, const memgraph::storage::View view, bool FineGrainedAuthChecker::Has(const memgraph::query::VertexAccessor &vertex, const memgraph::storage::View view,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const { const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
@@ -147,22 +200,22 @@ bool FineGrainedAuthChecker::Has(const memgraph::query::VertexAccessor &vertex,
} }
} }
return IsUserAuthorizedLabels(user_, dba_, *maybe_labels, fine_grained_privilege); return IsAuthorizedLabels(user_or_role_, dba_, *maybe_labels, fine_grained_privilege);
} }
bool FineGrainedAuthChecker::Has(const memgraph::query::EdgeAccessor &edge, bool FineGrainedAuthChecker::Has(const memgraph::query::EdgeAccessor &edge,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const { const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
return IsUserAuthorizedEdgeType(user_, dba_, edge.EdgeType(), fine_grained_privilege); return IsAuthorizedEdgeType(user_or_role_, dba_, edge.EdgeType(), fine_grained_privilege);
} }
bool FineGrainedAuthChecker::Has(const std::vector<memgraph::storage::LabelId> &labels, bool FineGrainedAuthChecker::Has(const std::vector<memgraph::storage::LabelId> &labels,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const { const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
return IsUserAuthorizedLabels(user_, dba_, labels, fine_grained_privilege); return IsAuthorizedLabels(user_or_role_, dba_, labels, fine_grained_privilege);
} }
bool FineGrainedAuthChecker::Has(const memgraph::storage::EdgeTypeId &edge_type, bool FineGrainedAuthChecker::Has(const memgraph::storage::EdgeTypeId &edge_type,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const { const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
return IsUserAuthorizedEdgeType(user_, dba_, edge_type, fine_grained_privilege); return IsAuthorizedEdgeType(user_or_role_, dba_, edge_type, fine_grained_privilege);
} }
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices( bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
@@ -170,7 +223,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return true; return true;
} }
return IsUserAuthorizedGloballyLabels(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege)); return IsAuthorizedGloballyLabels(user_or_role_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
} }
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges( bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
@@ -178,7 +231,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
return true; return true;
} }
return IsUserAuthorizedGloballyEdges(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege)); return IsAuthorizedGloballyEdges(user_or_role_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
}; };
#endif #endif
} // namespace memgraph::glue } // namespace memgraph::glue

View File

@@ -22,53 +22,59 @@ namespace memgraph::glue {
class AuthChecker : public query::AuthChecker { class AuthChecker : public query::AuthChecker {
public: public:
explicit AuthChecker(memgraph::auth::SynchedAuth *auth); explicit AuthChecker(auth::SynchedAuth *auth);
bool IsUserAuthorized(const std::optional<std::string> &username, std::shared_ptr<query::QueryUserOrRole> GenQueryUser(const std::optional<std::string> &username,
const std::vector<query::AuthQuery::Privilege> &privileges, const std::optional<std::string> &rolename) const override;
const std::string &db_name) const override;
static std::unique_ptr<query::QueryUserOrRole> GenQueryUser(auth::SynchedAuth *auth,
const std::optional<auth::UserOrRole> &user_or_role);
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> GetFineGrainedAuthChecker( std::unique_ptr<query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(std::shared_ptr<query::QueryUserOrRole> user,
const std::string &username, const memgraph::query::DbAccessor *dba) const override; const query::DbAccessor *dba) const override;
void ClearCache() const override;
#endif #endif
[[nodiscard]] static bool IsUserAuthorized(const memgraph::auth::User &user,
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges, [[nodiscard]] static bool IsUserAuthorized(const auth::User &user,
const std::vector<query::AuthQuery::Privilege> &privileges,
const std::string &db_name = ""); const std::string &db_name = "");
[[nodiscard]] static bool IsRoleAuthorized(const auth::Role &role,
const std::vector<query::AuthQuery::Privilege> &privileges,
const std::string &db_name = "");
[[nodiscard]] static bool IsUserOrRoleAuthorized(const auth::UserOrRole &user_or_role,
const std::vector<query::AuthQuery::Privilege> &privileges,
const std::string &db_name = "");
private: private:
memgraph::auth::SynchedAuth *auth_; auth::SynchedAuth *auth_;
mutable memgraph::utils::Synchronized<auth::User, memgraph::utils::SpinLock> user_; // cached user mutable utils::Synchronized<auth::UserOrRole, utils::SpinLock> user_or_role_; // cached user
}; };
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker { class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
public: public:
explicit FineGrainedAuthChecker(auth::User user, const memgraph::query::DbAccessor *dba); explicit FineGrainedAuthChecker(auth::UserOrRole user, const query::DbAccessor *dba);
bool Has(const query::VertexAccessor &vertex, memgraph::storage::View view, bool Has(const query::VertexAccessor &vertex, storage::View view,
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override; query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
bool Has(const query::EdgeAccessor &edge, bool Has(const query::EdgeAccessor &edge,
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override; query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
bool Has(const std::vector<memgraph::storage::LabelId> &labels, bool Has(const std::vector<storage::LabelId> &labels,
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override; query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
bool Has(const memgraph::storage::EdgeTypeId &edge_type, bool Has(const storage::EdgeTypeId &edge_type,
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override; query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
bool HasGlobalPrivilegeOnVertices( bool HasGlobalPrivilegeOnVertices(query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
bool HasGlobalPrivilegeOnEdges( bool HasGlobalPrivilegeOnEdges(query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
private: private:
auth::User user_; auth::UserOrRole user_or_role_;
const memgraph::query::DbAccessor *dba_; const query::DbAccessor *dba_;
}; };
#endif #endif
} // namespace memgraph::glue } // namespace memgraph::glue

View File

@@ -15,6 +15,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "auth/auth.hpp"
#include "auth/models.hpp" #include "auth/models.hpp"
#include "dbms/constants.hpp" #include "dbms/constants.hpp"
#include "glue/auth.hpp" #include "glue/auth.hpp"
@@ -123,6 +124,29 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowRolePrivileges(
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
const std::optional<memgraph::auth::Role> &role) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !role) {
return {};
}
const auto &db = role->db_access();
const auto &allows = db.GetAllowAll();
const auto &grants = db.GetGrants();
const auto &denies = db.GetDenies();
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
if (allows) {
res.emplace_back("*");
} else {
std::vector<memgraph::query::TypedValue> grants_vec(grants.cbegin(), grants.cend());
res.emplace_back(std::move(grants_vec));
}
std::vector<memgraph::query::TypedValue> denies_vec(denies.cbegin(), denies.cend());
res.emplace_back(std::move(denies_vec));
return {res};
}
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges( std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
const std::optional<memgraph::auth::User> &user) { const std::optional<memgraph::auth::User> &user) {
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !user) { if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !user) {
@@ -130,9 +154,15 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
} }
const auto &db = user->db_access(); const auto &db = user->db_access();
const auto &allows = db.GetAllowAll(); auto allows = db.GetAllowAll();
const auto &grants = db.GetGrants(); auto grants = db.GetGrants();
const auto &denies = db.GetDenies(); auto denies = db.GetDenies();
if (const auto *role = user->role()) {
const auto &role_db = role->db_access();
allows |= role_db.GetAllowAll();
grants.insert(role_db.GetGrants().begin(), role_db.GetGrants().end());
denies.insert(role_db.GetDenies().begin(), role_db.GetDenies().end());
}
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
if (allows) { if (allows) {
@@ -287,7 +317,7 @@ bool AuthQueryHandler::CreateUser(const std::string &username, const std::option
, ,
system_tx); system_tx);
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
GrantDatabaseToUser(auth::kAllDatabases, username, system_tx); GrantDatabase(auth::kAllDatabases, username, system_tx);
SetMainDatabase(dbms::kDefaultDB, username, system_tx); SetMainDatabase(dbms::kDefaultDB, username, system_tx);
#endif #endif
} }
@@ -334,51 +364,97 @@ bool AuthQueryHandler::CreateRole(const std::string &rolename, system::Transacti
} }
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
bool AuthQueryHandler::RevokeDatabaseFromUser(const std::string &db_name, const std::string &username, void AuthQueryHandler::GrantDatabase(const std::string &db_name, const std::string &user_or_role,
system::Transaction *system_tx) { system::Transaction *system_tx) {
try { try {
auto locked_auth = auth_->Lock(); auto locked_auth = auth_->Lock();
auto user = locked_auth->GetUser(username); const auto res = locked_auth->GrantDatabase(db_name, user_or_role, system_tx);
if (!user) return false; switch (res) {
return locked_auth->RevokeDatabaseFromUser(db_name, username, system_tx); using enum auth::Auth::Result;
case SUCCESS:
return;
case NO_USER_ROLE:
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
case NO_ROLE:
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
break;
}
} catch (const memgraph::auth::AuthException &e) { } catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what()); throw memgraph::query::QueryRuntimeException(e.what());
} }
} }
bool AuthQueryHandler::GrantDatabaseToUser(const std::string &db_name, const std::string &username, void AuthQueryHandler::DenyDatabase(const std::string &db_name, const std::string &user_or_role,
system::Transaction *system_tx) { system::Transaction *system_tx) {
try { try {
auto locked_auth = auth_->Lock(); auto locked_auth = auth_->Lock();
auto user = locked_auth->GetUser(username); const auto res = locked_auth->DenyDatabase(db_name, user_or_role, system_tx);
if (!user) return false; switch (res) {
return locked_auth->GrantDatabaseToUser(db_name, username, system_tx); using enum auth::Auth::Result;
case SUCCESS:
return;
case NO_USER_ROLE:
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
case NO_ROLE:
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
break;
}
} catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what());
}
}
void AuthQueryHandler::RevokeDatabase(const std::string &db_name, const std::string &user_or_role,
system::Transaction *system_tx) {
try {
auto locked_auth = auth_->Lock();
const auto res = locked_auth->RevokeDatabase(db_name, user_or_role, system_tx);
switch (res) {
using enum auth::Auth::Result;
case SUCCESS:
return;
case NO_USER_ROLE:
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
case NO_ROLE:
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
break;
}
} catch (const memgraph::auth::AuthException &e) { } catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what()); throw memgraph::query::QueryRuntimeException(e.what());
} }
} }
std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges( std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges(
const std::string &username) { const std::string &user_or_role) {
try { try {
auto locked_auth = auth_->ReadLock(); auto locked_auth = auth_->ReadLock();
auto user = locked_auth->GetUser(username); if (auto user = locked_auth->GetUser(user_or_role)) {
if (!user) { return ShowDatabasePrivileges(user);
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist.", username);
} }
return ShowDatabasePrivileges(user); if (auto role = locked_auth->GetRole(user_or_role)) {
return ShowDatabasePrivileges(role);
}
throw memgraph::query::QueryRuntimeException("Neither user nor role '{}' exist.", user_or_role);
} catch (const memgraph::auth::AuthException &e) { } catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what()); throw memgraph::query::QueryRuntimeException(e.what());
} }
} }
bool AuthQueryHandler::SetMainDatabase(std::string_view db_name, const std::string &username, void AuthQueryHandler::SetMainDatabase(std::string_view db_name, const std::string &user_or_role,
system::Transaction *system_tx) { system::Transaction *system_tx) {
try { try {
auto locked_auth = auth_->Lock(); auto locked_auth = auth_->Lock();
auto user = locked_auth->GetUser(username); const auto res = locked_auth->SetMainDatabase(db_name, user_or_role, system_tx);
if (!user) return false; switch (res) {
return locked_auth->SetMainDatabase(db_name, username, system_tx); using enum auth::Auth::Result;
case SUCCESS:
return;
case NO_USER_ROLE:
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
case NO_ROLE:
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
break;
}
} catch (const memgraph::auth::AuthException &e) { } catch (const memgraph::auth::AuthException &e) {
throw memgraph::query::QueryRuntimeException(e.what()); throw memgraph::query::QueryRuntimeException(e.what());
} }

View File

@@ -37,15 +37,19 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
system::Transaction *system_tx) override; system::Transaction *system_tx) override;
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
bool RevokeDatabaseFromUser(const std::string &db_name, const std::string &username, void GrantDatabase(const std::string &db_name, const std::string &user_or_role,
system::Transaction *system_tx) override; system::Transaction *system_tx) override;
bool GrantDatabaseToUser(const std::string &db_name, const std::string &username, void DenyDatabase(const std::string &db_name, const std::string &user_or_role,
system::Transaction *system_tx) override; system::Transaction *system_tx) override;
std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &username) override; void RevokeDatabase(const std::string &db_name, const std::string &user_or_role,
system::Transaction *system_tx) override;
bool SetMainDatabase(std::string_view db_name, const std::string &username, system::Transaction *system_tx) override; std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &user_or_role) override;
void SetMainDatabase(std::string_view db_name, const std::string &user_or_role,
system::Transaction *system_tx) override;
void DeleteDatabase(std::string_view db_name, system::Transaction *system_tx) override; void DeleteDatabase(std::string_view db_name, system::Transaction *system_tx) override;
#endif #endif

41
src/glue/query_user.cpp Normal file
View File

@@ -0,0 +1,41 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "glue/query_user.hpp"
#include "glue/auth_checker.hpp"
namespace memgraph::glue {
bool QueryUserOrRole::IsAuthorized(const std::vector<query::AuthQuery::Privilege> &privileges,
const std::string &db_name, query::UserPolicy *policy) const {
auto locked_auth = auth_->Lock();
// Check policy and update if behind (and policy permits it)
if (policy->DoUpdate() && !locked_auth->UpToDate(auth_epoch_)) {
if (user_) user_ = locked_auth->GetUser(user_->username());
if (role_) role_ = locked_auth->GetRole(role_->rolename());
}
if (user_) return AuthChecker::IsUserAuthorized(*user_, privileges, db_name);
if (role_) return AuthChecker::IsRoleAuthorized(*role_, privileges, db_name);
return !policy->DoUpdate() || !locked_auth->AccessControlled();
}
#ifdef MG_ENTERPRISE
std::string QueryUserOrRole::GetDefaultDB() const {
if (user_) return user_->db_access().GetMain();
if (role_) return role_->db_access().GetMain();
return std::string{dbms::kDefaultDB};
}
#endif
} // namespace memgraph::glue

57
src/glue/query_user.hpp Normal file
View File

@@ -0,0 +1,57 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <optional>
#include "auth/auth.hpp"
#include "query/query_user.hpp"
#include "utils/variant_helpers.hpp"
namespace memgraph::glue {
struct QueryUserOrRole : public query::QueryUserOrRole {
bool IsAuthorized(const std::vector<query::AuthQuery::Privilege> &privileges, const std::string &db_name,
query::UserPolicy *policy) const override;
#ifdef MG_ENTERPRISE
std::string GetDefaultDB() const override;
#endif
explicit QueryUserOrRole(auth::SynchedAuth *auth) : query::QueryUserOrRole{std::nullopt, std::nullopt}, auth_{auth} {}
QueryUserOrRole(auth::SynchedAuth *auth, auth::UserOrRole user_or_role)
: query::QueryUserOrRole{std::visit(
utils::Overloaded{[](const auto &user_or_role) { return user_or_role.username(); }},
user_or_role),
std::visit(utils::Overloaded{[&](const auth::User &) -> std::optional<std::string> {
return std::nullopt;
},
[&](const auth::Role &role) -> std::optional<std::string> {
return role.rolename();
}},
user_or_role)},
auth_{auth} {
std::visit(utils::Overloaded{[&](auth::User &&user) { user_.emplace(std::move(user)); },
[&](auth::Role &&role) { role_.emplace(std::move(role)); }},
std::move(user_or_role));
}
private:
friend class AuthChecker;
auth::SynchedAuth *auth_;
mutable std::optional<auth::User> user_{};
mutable std::optional<auth::Role> role_{};
mutable auth::Auth::Epoch auth_epoch_{auth::Auth::kStartEpoch};
};
} // namespace memgraph::glue

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -22,6 +22,7 @@
#include "integrations/constants.hpp" #include "integrations/constants.hpp"
#include "integrations/kafka/exceptions.hpp" #include "integrations/kafka/exceptions.hpp"
#include "integrations/kafka/fmt.hpp"
#include "utils/exceptions.hpp" #include "utils/exceptions.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/on_scope_exit.hpp" #include "utils/on_scope_exit.hpp"

View File

@@ -0,0 +1,25 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#if FMT_VERSION > 90000
#include <fmt/ostream.h>
#include <librdkafka/rdkafkacpp.h>
inline std::ostream &operator<<(std::ostream &os, const RdKafka::ErrorCode &code) {
os << RdKafka::err2str(code);
return os;
}
template <>
class fmt::formatter<RdKafka::ErrorCode> : public fmt::ostream_formatter {};
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -15,12 +15,12 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <fmt/format.h>
#include <pulsar/Client.h> #include <pulsar/Client.h>
#include <pulsar/InitialPosition.h> #include <pulsar/InitialPosition.h>
#include "integrations/constants.hpp" #include "integrations/constants.hpp"
#include "integrations/pulsar/exceptions.hpp" #include "integrations/pulsar/exceptions.hpp"
#include "integrations/pulsar/fmt.hpp"
#include "utils/concepts.hpp" #include "utils/concepts.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/on_scope_exit.hpp" #include "utils/on_scope_exit.hpp"

View File

@@ -0,0 +1,21 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#if FMT_VERSION > 90000
#include <fmt/ostream.h>
#include "integrations/pulsar/consumer.hpp"
template <>
class fmt::formatter<memgraph::integrations::pulsar::pulsar_client::Result> : public fmt::ostream_formatter {};
#endif

21
src/io/network/fmt.hpp Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2024 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#if FMT_VERSION > 90000
#include <fmt/ostream.h>
#include "io/network/endpoint.hpp"
template <>
class fmt::formatter<memgraph::io::network::Endpoint> : public fmt::ostream_formatter {};
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd. // Copyright 2023 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,6 +11,7 @@
#pragma once #pragma once
#include <cstddef>
#include <cstdint> #include <cstdint>
namespace memgraph::io::network { namespace memgraph::io::network {

View File

@@ -160,13 +160,14 @@ class KVStore final {
* and behaves as if all of those pairs are stored in a single iterable * and behaves as if all of those pairs are stored in a single iterable
* collection of std::pair<std::string, std::string>. * collection of std::pair<std::string, std::string>.
*/ */
class iterator final : public std::iterator<std::input_iterator_tag, // iterator_category class iterator final {
std::pair<std::string, std::string>, // value_type
long, // difference_type
const std::pair<std::string, std::string> *, // pointer
const std::pair<std::string, std::string> & // reference
> {
public: public:
using iterator_concept [[maybe_unused]] = std::input_iterator_tag;
using value_type = std::pair<std::string, std::string>;
using difference_type = long;
using pointer = const std::pair<std::string, std::string> *;
using reference = const std::pair<std::string, std::string> &;
explicit iterator(const KVStore *kvstore, const std::string &prefix = "", bool at_end = false); explicit iterator(const KVStore *kvstore, const std::string &prefix = "", bool at_end = false);
iterator(const iterator &other) = delete; iterator(const iterator &other) = delete;

View File

@@ -27,6 +27,7 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "license/license_sender.hpp" #include "license/license_sender.hpp"
#include "memory/global_memory_control.hpp" #include "memory/global_memory_control.hpp"
#include "query/auth_checker.hpp"
#include "query/auth_query_handler.hpp" #include "query/auth_query_handler.hpp"
#include "query/config.hpp" #include "query/config.hpp"
#include "query/discard_value_stream.hpp" #include "query/discard_value_stream.hpp"
@@ -57,8 +58,13 @@ constexpr uint64_t kMgVmMaxMapCount = 262144;
void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, memgraph::dbms::DatabaseAccess &db_acc, void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, memgraph::dbms::DatabaseAccess &db_acc,
std::string cypherl_file_path, memgraph::audit::Log *audit_log = nullptr) { std::string cypherl_file_path, memgraph::audit::Log *audit_log = nullptr) {
memgraph::query::Interpreter interpreter(&ctx, db_acc); memgraph::query::Interpreter interpreter(&ctx, db_acc);
std::ifstream file(cypherl_file_path); // Temporary empty user
// TODO: Double check with buda
memgraph::query::AllowEverythingAuthChecker tmp_auth_checker;
auto tmp_user = tmp_auth_checker.GenQueryUser(std::nullopt, std::nullopt);
interpreter.SetUser(tmp_user);
std::ifstream file(cypherl_file_path);
if (!file.is_open()) { if (!file.is_open()) {
spdlog::trace("Could not find init file {}", cypherl_file_path); spdlog::trace("Could not find init file {}", cypherl_file_path);
return; return;
@@ -359,6 +365,7 @@ int main(int argc, char **argv) {
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
.instance_down_timeout_sec = std::chrono::seconds(FLAGS_instance_down_timeout_sec), .instance_down_timeout_sec = std::chrono::seconds(FLAGS_instance_down_timeout_sec),
.instance_health_check_frequency_sec = std::chrono::seconds(FLAGS_instance_health_check_frequency_sec), .instance_health_check_frequency_sec = std::chrono::seconds(FLAGS_instance_health_check_frequency_sec),
.instance_get_uuid_frequency_sec = std::chrono::seconds(FLAGS_instance_get_uuid_frequency_sec),
#endif #endif
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers, .default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
.default_pulsar_service_url = FLAGS_pulsar_service_url, .default_pulsar_service_url = FLAGS_pulsar_service_url,

View File

@@ -61,10 +61,12 @@ void *my_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size, size_t
// This needs to be before, to throw exception in case of too big alloc // This needs to be before, to throw exception in case of too big alloc
if (*commit) [[likely]] { if (*commit) [[likely]] {
if (GetQueriesMemoryControl().IsThreadTracked()) [[unlikely]] { if (GetQueriesMemoryControl().IsThreadTracked()) [[unlikely]] {
GetQueriesMemoryControl().TrackAllocOnCurrentThread(size); bool ok = GetQueriesMemoryControl().TrackAllocOnCurrentThread(size);
if (!ok) return nullptr;
} }
// This needs to be here so it doesn't get incremented in case the first TrackAlloc throws an exception // This needs to be here so it doesn't get incremented in case the first TrackAlloc throws an exception
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size)); bool ok = memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(size));
if (!ok) return nullptr;
} }
auto *ptr = old_hooks->alloc(extent_hooks, new_addr, size, alignment, zero, commit, arena_ind); auto *ptr = old_hooks->alloc(extent_hooks, new_addr, size, alignment, zero, commit, arena_ind);
@@ -118,10 +120,14 @@ static bool my_commit(extent_hooks_t *extent_hooks, void *addr, size_t size, siz
return err; return err;
} }
[[maybe_unused]] auto blocker = memgraph::utils::MemoryTracker::OutOfMemoryExceptionBlocker{};
if (GetQueriesMemoryControl().IsThreadTracked()) [[unlikely]] { if (GetQueriesMemoryControl().IsThreadTracked()) [[unlikely]] {
GetQueriesMemoryControl().TrackAllocOnCurrentThread(length); bool ok = GetQueriesMemoryControl().TrackAllocOnCurrentThread(length);
DMG_ASSERT(ok);
} }
memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(length));
auto ok = memgraph::utils::total_memory_tracker.Alloc(static_cast<int64_t>(length));
DMG_ASSERT(ok);
return false; return false;
} }

View File

@@ -1,4 +1,4 @@
// Copyright 2023 Memgraph Ltd. // Copyright 2024 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -28,6 +28,12 @@ void *newImpl(const std::size_t size) {
return ptr; return ptr;
} }
[[maybe_unused]] auto blocker = memgraph::utils::MemoryTracker::OutOfMemoryExceptionBlocker{};
auto maybe_msg = memgraph::utils::MemoryErrorStatus().msg();
if (maybe_msg) {
throw memgraph::utils::OutOfMemoryException{std::move(*maybe_msg)};
}
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
@@ -37,11 +43,21 @@ void *newImpl(const std::size_t size, const std::align_val_t align) {
return ptr; return ptr;
} }
[[maybe_unused]] auto blocker = memgraph::utils::MemoryTracker::OutOfMemoryExceptionBlocker{};
auto maybe_msg = memgraph::utils::MemoryErrorStatus().msg();
if (maybe_msg) {
throw memgraph::utils::OutOfMemoryException{std::move(*maybe_msg)};
}
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
void *newNoExcept(const std::size_t size) noexcept { return malloc(size); } void *newNoExcept(const std::size_t size) noexcept {
[[maybe_unused]] auto blocker = memgraph::utils::MemoryTracker::OutOfMemoryExceptionBlocker{};
return malloc(size);
}
void *newNoExcept(const std::size_t size, const std::align_val_t align) noexcept { void *newNoExcept(const std::size_t size, const std::align_val_t align) noexcept {
[[maybe_unused]] auto blocker = memgraph::utils::MemoryTracker::OutOfMemoryExceptionBlocker{};
return aligned_alloc(size, static_cast<std::size_t>(align)); return aligned_alloc(size, static_cast<std::size_t>(align));
} }

View File

@@ -54,14 +54,14 @@ void QueriesMemoryControl::EraseThreadToTransactionId(const std::thread::id &thr
} }
} }
void QueriesMemoryControl::TrackAllocOnCurrentThread(size_t size) { bool QueriesMemoryControl::TrackAllocOnCurrentThread(size_t size) {
auto thread_id_to_transaction_id_accessor = thread_id_to_transaction_id.access(); auto thread_id_to_transaction_id_accessor = thread_id_to_transaction_id.access();
// we might be just constructing mapping between thread id and transaction id // we might be just constructing mapping between thread id and transaction id
// so we miss this allocation // so we miss this allocation
auto thread_id_to_transaction_id_elem = thread_id_to_transaction_id_accessor.find(std::this_thread::get_id()); auto thread_id_to_transaction_id_elem = thread_id_to_transaction_id_accessor.find(std::this_thread::get_id());
if (thread_id_to_transaction_id_elem == thread_id_to_transaction_id_accessor.end()) { if (thread_id_to_transaction_id_elem == thread_id_to_transaction_id_accessor.end()) {
return; return true;
} }
auto transaction_id_to_tracker_accessor = transaction_id_to_tracker.access(); auto transaction_id_to_tracker_accessor = transaction_id_to_tracker.access();
@@ -71,10 +71,10 @@ void QueriesMemoryControl::TrackAllocOnCurrentThread(size_t size) {
// It can happen that some allocation happens between mapping thread to // It can happen that some allocation happens between mapping thread to
// transaction id, so we miss this allocation // transaction id, so we miss this allocation
if (transaction_id_to_tracker == transaction_id_to_tracker_accessor.end()) [[unlikely]] { if (transaction_id_to_tracker == transaction_id_to_tracker_accessor.end()) [[unlikely]] {
return; return true;
} }
auto &query_tracker = transaction_id_to_tracker->tracker; auto &query_tracker = transaction_id_to_tracker->tracker;
query_tracker.TrackAlloc(size); return query_tracker.TrackAlloc(size);
} }
void QueriesMemoryControl::TrackFreeOnCurrentThread(size_t size) { void QueriesMemoryControl::TrackFreeOnCurrentThread(size_t size) {

View File

@@ -62,7 +62,7 @@ class QueriesMemoryControl {
// Find tracker for current thread if exists, track // Find tracker for current thread if exists, track
// query allocation and procedure allocation if // query allocation and procedure allocation if
// necessary // necessary
void TrackAllocOnCurrentThread(size_t size); bool TrackAllocOnCurrentThread(size_t size);
// Find tracker for current thread if exists, track // Find tracker for current thread if exists, track
// query allocation and procedure allocation if // query allocation and procedure allocation if

View File

@@ -139,6 +139,11 @@ struct NodeId {
std::string id_space; std::string id_space;
}; };
#if FMT_VERSION > 90000
template <>
class fmt::formatter<NodeId> : public fmt::ostream_formatter {};
#endif
bool operator==(const NodeId &a, const NodeId &b) { return a.id == b.id && a.id_space == b.id_space; } bool operator==(const NodeId &a, const NodeId &b) { return a.id == b.id && a.id_space == b.id_space; }
std::ostream &operator<<(std::ostream &stream, const NodeId &node_id) { std::ostream &operator<<(std::ostream &stream, const NodeId &node_id) {

Some files were not shown because too many files have changed in this diff Show More