Merge branch 'master' into fix-valuetype-function-on-all-data-types
This commit is contained in:
commit
2cc310703e
47
.github/workflows/diff.yaml
vendored
47
.github/workflows/diff.yaml
vendored
@ -336,53 +336,6 @@ jobs:
|
||||
# multiple paths could be defined
|
||||
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:
|
||||
name: "Release Jepsen Test"
|
||||
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]
|
||||
|
162
.github/workflows/release_build_test.yaml
vendored
Normal file
162
.github/workflows/release_build_test.yaml
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
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 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 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: 90
|
||||
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
|
||||
|
||||
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 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: 90
|
||||
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
|
||||
|
||||
PushToS3:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [PackageDebian10, PackageDebian11, PackageDebian11_ARM, PackageUbuntu20_04, PackageUbuntu20_04_ARM]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
# name: # if name input parameter is not provided, all artifacts are downloaded
|
||||
# and put in directories named after each one.
|
||||
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 }}/"
|
28
.github/workflows/release_debian10.yaml
vendored
28
.github/workflows/release_debian10.yaml
vendored
@ -1,9 +1,12 @@
|
||||
name: Release Debian 10
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
type: string
|
||||
description: "Memgraph Build type. Default value is Release."
|
||||
default: 'Release'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
@ -13,16 +16,9 @@ on:
|
||||
options:
|
||||
- Release
|
||||
- RelWithDebInfo
|
||||
push:
|
||||
branches:
|
||||
- "release/**"
|
||||
tags:
|
||||
- "v*.*.*-rc*"
|
||||
- "v*.*-rc*"
|
||||
schedule:
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
env:
|
||||
OS: "Debian10"
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
@ -119,7 +115,7 @@ jobs:
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Coverage build)"
|
||||
name: "Code coverage(Coverage build)-${{ env.OS }}"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
|
||||
debug_build:
|
||||
@ -173,7 +169,7 @@ jobs:
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Debug build)"
|
||||
name: "Code coverage(Debug build)-${{ env.OS }}"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
|
||||
debug_integration_test:
|
||||
@ -250,7 +246,7 @@ jobs:
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Enterprise DEB package"
|
||||
name: "Enterprise DEB package-${{ env.OS}}"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Run GQL Behave tests
|
||||
@ -263,7 +259,7 @@ jobs:
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "GQL Behave Status"
|
||||
name: "GQL Behave Status-${{ env.OS }}"
|
||||
path: |
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
@ -463,5 +459,5 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: "Jepsen Report"
|
||||
name: "Jepsen Report-${{ env.OS }}"
|
||||
path: tests/jepsen/Jepsen.tar.gz
|
||||
|
26
.github/workflows/release_ubuntu2004.yaml
vendored
26
.github/workflows/release_ubuntu2004.yaml
vendored
@ -1,9 +1,12 @@
|
||||
name: Release Ubuntu 20.04
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
type: string
|
||||
description: "Memgraph Build type. Default value is Release."
|
||||
default: 'Release'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
@ -13,16 +16,9 @@ on:
|
||||
options:
|
||||
- Release
|
||||
- RelWithDebInfo
|
||||
push:
|
||||
branches:
|
||||
- "release/**"
|
||||
tags:
|
||||
- "v*.*.*-rc*"
|
||||
- "v*.*-rc*"
|
||||
schedule:
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
env:
|
||||
OS: "Ubuntu 20.04"
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
@ -115,7 +111,7 @@ jobs:
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Coverage build)"
|
||||
name: "Code coverage(Coverage build)-${{ env.OS }}"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
|
||||
debug_build:
|
||||
@ -169,7 +165,7 @@ jobs:
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Debug build)"
|
||||
name: "Code coverage(Debug build)-${{ env.OS }}"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
|
||||
debug_integration_test:
|
||||
@ -246,7 +242,7 @@ jobs:
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Enterprise DEB package"
|
||||
name: "Enterprise DEB package-${{ env.OS }}"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Run GQL Behave tests
|
||||
@ -259,7 +255,7 @@ jobs:
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "GQL Behave Status"
|
||||
name: "GQL Behave Status-${{ env.OS }}"
|
||||
path: |
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
|
@ -189,7 +189,7 @@ add_custom_target(clean_all
|
||||
# is easier debugging of compilation and linker flags.
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
# c99-designator is disabled because of required mixture of designated and
|
||||
# non-designated initializers in Python Query Module code (`py_module.cpp`).
|
||||
@ -211,8 +211,13 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
# ** Static linking is allowed only for executables! **
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||
|
||||
# Use lld linker to speedup build
|
||||
add_link_options(-fuse-ld=lld) # TODO: use mold linker
|
||||
# Use lld linker to speedup build and use less memory.
|
||||
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
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
@ -271,18 +276,6 @@ endif()
|
||||
set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
|
||||
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(TOOLS "Build tools binaries" ON)
|
||||
option(QUERY_MODULES "Build query modules containing custom procedures" ON)
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -9,7 +7,7 @@ check_operating_system "amzn-2"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
gcc gcc-c++ make # generic build tools
|
||||
git 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
|
||||
@ -63,6 +61,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -63,6 +61,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -9,8 +7,10 @@ check_operating_system "centos-9"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
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
|
||||
@ -64,6 +64,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
@ -123,7 +125,9 @@ install() {
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
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
|
||||
|
||||
for pkg in $1; do
|
||||
|
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "debian-10"
|
||||
check_architecture "x86_64"
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "debian-11"
|
||||
check_architecture "arm64" "aarch64"
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -61,6 +59,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
134
environment/os/debian-12-arm.sh
Executable file
134
environment/os/debian-12-arm.sh
Executable 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
136
environment/os/debian-12.sh
Executable 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}"
|
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "fedora-36"
|
||||
check_architecture "x86_64"
|
||||
|
||||
@ -27,6 +27,7 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
git
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -27,6 +25,7 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
git
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
@ -58,6 +57,16 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
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"
|
||||
}
|
||||
|
117
environment/os/fedora-39.sh
Executable file
117
environment/os/fedora-39.sh
Executable 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
188
environment/os/rocky-9.3.sh
Executable 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}"
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -20,6 +18,10 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "ubuntu-18.04"
|
||||
check_architecture "x86_64"
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -60,6 +58,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp2
|
||||
)
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -60,6 +58,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp2
|
||||
)
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@ -60,6 +58,8 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp2
|
||||
)
|
||||
|
1
environment/toolchain/.gitignore
vendored
1
environment/toolchain/.gitignore
vendored
@ -2,3 +2,4 @@ archives
|
||||
build
|
||||
output
|
||||
*.tar.gz
|
||||
tmp_build.sh
|
||||
|
48
environment/toolchain/template_build.sh
Normal file
48
environment/toolchain/template_build.sh
Normal 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.
|
@ -307,7 +307,7 @@ if [ ! -f $PREFIX/bin/ld.gold ]; then
|
||||
fi
|
||||
|
||||
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
|
||||
rm -rf gdb-$GDB_VERSION
|
||||
fi
|
||||
@ -671,7 +671,6 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
|
||||
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
||||
# WANGLE_SHA256=0e493c03572bb27fe9ca03a9da5023e52fde99c95abdcaa919bb6190e7e69532
|
||||
|
||||
FLEX_VERSION=2.6.4
|
||||
FMT_SHA256=78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b
|
||||
FMT_VERSION=10.1.1
|
||||
# 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_VERSION=1.1.9
|
||||
XZ_VERSION=5.2.5 # for LZMA
|
||||
ZLIB_VERSION=1.3
|
||||
ZSTD_VERSION=1.5.0
|
||||
ZLIB_VERSION=1.3.1
|
||||
ZSTD_VERSION=1.5.5
|
||||
|
||||
pushd archives
|
||||
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
|
||||
fi
|
||||
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
|
||||
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
|
||||
@ -708,9 +707,7 @@ fi
|
||||
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
|
||||
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
|
||||
wget https://github.com/fmtlib/fmt/archive/refs/tags/$FMT_VERSION.tar.gz -O fmt-$FMT_VERSION.tar.gz
|
||||
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
|
||||
# verify fizz
|
||||
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
|
||||
echo "$FMT_SHA256 fmt-$FMT_VERSION.tar.gz" | sha256sum -c
|
||||
# verify spdlog
|
||||
@ -1025,7 +1014,6 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
||||
if [ -d gflags ]; then
|
||||
rm -rf gflags
|
||||
fi
|
||||
|
||||
git clone https://github.com/memgraph/gflags.git gflags
|
||||
pushd gflags
|
||||
git checkout $GFLAGS_COMMIT_HASH
|
||||
@ -1034,7 +1022,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
||||
cmake .. $COMMON_CMAKE_FLAGS \
|
||||
-DREGISTER_INSTALL_PREFIX=OFF \
|
||||
-DBUILD_gflags_nothreads_LIB=OFF \
|
||||
-DGFLAGS_NO_FILENAMES=0
|
||||
-DGFLAGS_NO_FILENAMES=1
|
||||
make -j$CPUS install
|
||||
popd && popd
|
||||
fi
|
||||
@ -1232,18 +1220,6 @@ if false; then
|
||||
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
|
||||
# NOTE: It's important/clean (e.g., easier upload to S3) to have a separated
|
||||
# folder to the output archive.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -283,7 +283,7 @@ inline mgp_list *list_all_unique_constraints(mgp_graph *graph, mgp_memory *memor
|
||||
}
|
||||
|
||||
// mgp_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); }
|
||||
|
1
libs/.gitignore
vendored
1
libs/.gitignore
vendored
@ -7,3 +7,4 @@
|
||||
!pulsar.patch
|
||||
!antlr4.10.1.patch
|
||||
!rocksdb8.1.1.patch
|
||||
!nuraft2.1.0.patch
|
||||
|
@ -16,7 +16,7 @@ set(GFLAGS_NOTHREADS OFF)
|
||||
|
||||
# NOTE: config/generate.py depends on the gflags help XML format.
|
||||
find_package(gflags REQUIRED)
|
||||
find_package(fmt 8.0.1)
|
||||
find_package(fmt 8.0.1 REQUIRED)
|
||||
find_package(ZLIB 1.2.11 REQUIRED)
|
||||
|
||||
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
@ -5,7 +5,7 @@ index ee9b58c..31359a9 100644
|
||||
@@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF)
|
||||
# | Library Build and Install Properties |
|
||||
# +--------------------------------------------------------+
|
||||
|
||||
|
||||
-add_library(rdtsc SHARED
|
||||
+add_library(rdtsc
|
||||
src/cycles.c
|
||||
@ -14,7 +14,7 @@ index ee9b58c..31359a9 100644
|
||||
@@ -72,15 +72,6 @@ target_include_directories(rdtsc
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
|
||||
-# Install directory changes depending on build mode
|
||||
-if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
|
||||
- # 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)
|
||||
install(TARGETS rdtsc
|
||||
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
24
libs/nuraft2.1.0.patch
Normal 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>
|
||||
|
@ -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}"
|
@ -168,12 +168,11 @@ pushd antlr4
|
||||
git apply ../antlr4.10.1.patch
|
||||
popd
|
||||
|
||||
# cppitertools v2.0 2019-12-23
|
||||
cppitertools_ref="cb3635456bdb531121b82b4d2e3afc7ae1f56d47"
|
||||
cppitertools_ref="v2.1" # 2021-01-15
|
||||
repo_clone_try_double "${primary_urls[cppitertools]}" "${secondary_urls[cppitertools]}" "cppitertools" "$cppitertools_ref"
|
||||
|
||||
# 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"
|
||||
|
||||
# google benchmark
|
||||
@ -221,7 +220,7 @@ repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient
|
||||
mgconsole_tag="v1.4.0" # (2023-05-21)
|
||||
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
|
||||
|
||||
# librdkafka
|
||||
@ -286,5 +285,6 @@ repo_clone_try_double "${primary_urls[range-v3]}" "${secondary_urls[range-v3]}"
|
||||
nuraft_tag="v2.1.0"
|
||||
repo_clone_try_double "${primary_urls[nuraft]}" "${secondary_urls[nuraft]}" "nuraft" "$nuraft_tag" true
|
||||
pushd nuraft
|
||||
git apply ../nuraft2.1.0.patch
|
||||
./prepare.sh
|
||||
popd
|
||||
|
@ -35,16 +35,42 @@ DEFINE_VALIDATED_string(auth_module_executable, "", "Absolute path to the auth m
|
||||
}
|
||||
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,
|
||||
"Timeout (in milliseconds) used when waiting for a "
|
||||
"response from the auth module.",
|
||||
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 {
|
||||
|
||||
const Auth::Epoch Auth::kStartEpoch = 1;
|
||||
|
||||
namespace {
|
||||
#ifdef MG_ENTERPRISE
|
||||
/**
|
||||
@ -192,6 +218,17 @@ void MigrateVersions(kvstore::KVStore &store) {
|
||||
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
|
||||
|
||||
Auth::Auth(std::string storage_directory, Config config)
|
||||
@ -199,8 +236,11 @@ Auth::Auth(std::string storage_directory, Config config)
|
||||
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()) {
|
||||
/*
|
||||
* MODULE AUTH STORAGE
|
||||
*/
|
||||
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||
if (license_check_result.HasError()) {
|
||||
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>();
|
||||
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.
|
||||
if (!is_authenticated) return std::nullopt;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return RoleWUsername{username, std::move(*role)};
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
auto username = utils::ToLowerCase(username_orig);
|
||||
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);
|
||||
|
||||
void Auth::LinkUser(User &user) const {
|
||||
auto link = storage_.Get(kLinkPrefix + user.username());
|
||||
if (link) {
|
||||
auto role = GetRole(*link);
|
||||
if (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;
|
||||
}
|
||||
|
||||
void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||
DisableIfModuleUsed();
|
||||
bool success = false;
|
||||
if (const auto *role = user.role(); role != nullptr) {
|
||||
success = storage_.PutMultiple(
|
||||
@ -338,6 +334,10 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||
if (!success) {
|
||||
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
|
||||
if (system_tx) {
|
||||
#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) {
|
||||
DisableIfModuleUsed();
|
||||
// Check if null
|
||||
if (!password) {
|
||||
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,
|
||||
system::Transaction *system_tx) {
|
||||
DisableIfModuleUsed();
|
||||
if (!NameRegexMatch(username)) {
|
||||
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) {
|
||||
DisableIfModuleUsed();
|
||||
auto username = utils::ToLowerCase(username_orig);
|
||||
if (!storage_.Get(kUserPrefix + username)) return false;
|
||||
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
||||
if (!storage_.DeleteMultiple(keys)) {
|
||||
throw AuthException("Couldn't remove user '{}'!", username);
|
||||
}
|
||||
|
||||
// Durability updated -> new epoch
|
||||
UpdateEpoch();
|
||||
|
||||
// Handling drop user delta
|
||||
if (system_tx) {
|
||||
#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) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
if (username != utils::ToLowerCase(username)) continue;
|
||||
auto user = GetUser(username);
|
||||
if (user) {
|
||||
ret.push_back(std::move(*user));
|
||||
try {
|
||||
User user = auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||
LinkUser(user);
|
||||
ret.emplace_back(std::move(user));
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -425,9 +435,12 @@ std::vector<std::string> Auth::AllUsernames() const {
|
||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
if (username != utils::ToLowerCase(username)) continue;
|
||||
auto user = GetUser(username);
|
||||
if (user) {
|
||||
ret.push_back(username);
|
||||
try {
|
||||
// Check if serialized correctly
|
||||
memgraph::auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||
ret.emplace_back(std::move(username));
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
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::AccessControlled() const { return HasUsers() || module_.IsUsed(); }
|
||||
|
||||
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
|
||||
auto rolename = utils::ToLowerCase(rolename_orig);
|
||||
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
||||
if (!existing_role) return std::nullopt;
|
||||
|
||||
nlohmann::json data;
|
||||
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);
|
||||
return Role::Deserialize(ParseJson(*existing_role));
|
||||
}
|
||||
|
||||
void Auth::SaveRole(const Role &role, system::Transaction *system_tx) {
|
||||
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
||||
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
|
||||
if (system_tx) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
@ -486,6 +498,10 @@ bool Auth::RemoveRole(const std::string &rolename_orig, system::Transaction *sys
|
||||
if (!storage_.DeleteMultiple(keys)) {
|
||||
throw AuthException("Couldn't remove role '{}'!", rolename);
|
||||
}
|
||||
|
||||
// Durability updated -> new epoch
|
||||
UpdateEpoch();
|
||||
|
||||
// Handling drop role delta
|
||||
if (system_tx) {
|
||||
#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) {
|
||||
auto rolename = it->first.substr(kRolePrefix.size());
|
||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||
if (auto role = GetRole(rolename)) {
|
||||
ret.push_back(*role);
|
||||
} else {
|
||||
throw AuthException("Couldn't load role '{}'!", rolename);
|
||||
}
|
||||
Role role = memgraph::auth::Role::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||
ret.emplace_back(std::move(role));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -514,14 +527,19 @@ std::vector<std::string> Auth::AllRolenames() const {
|
||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||
auto rolename = it->first.substr(kRolePrefix.size());
|
||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||
if (auto role = GetRole(rolename)) {
|
||||
ret.push_back(rolename);
|
||||
try {
|
||||
// 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;
|
||||
}
|
||||
|
||||
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
||||
DisableIfModuleUsed();
|
||||
const auto rolename = utils::ToLowerCase(rolename_orig);
|
||||
std::vector<auth::User> ret;
|
||||
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
|
||||
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
if (auto user = GetUser(name)) {
|
||||
if (db == kAllDatabases) {
|
||||
user->db_access().GrantAll();
|
||||
} else {
|
||||
user->db_access().Add(db);
|
||||
Auth::Result Auth::GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
using enum Auth::Result;
|
||||
if (module_.IsUsed()) {
|
||||
if (auto role = GetRole(name)) {
|
||||
GrantDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
SaveUser(*user, system_tx);
|
||||
return true;
|
||||
return NO_ROLE;
|
||||
}
|
||||
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) {
|
||||
if (auto user = GetUser(name)) {
|
||||
if (db == kAllDatabases) {
|
||||
user->db_access().DenyAll();
|
||||
} else {
|
||||
user->db_access().Remove(db);
|
||||
}
|
||||
SaveUser(*user, system_tx);
|
||||
return true;
|
||||
void Auth::GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
user.db_access().GrantAll();
|
||||
} else {
|
||||
user.db_access().Grant(db);
|
||||
}
|
||||
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) {
|
||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
if (auto user = GetUser(username)) {
|
||||
user->db_access().Delete(db);
|
||||
SaveUser(*user, system_tx);
|
||||
try {
|
||||
User user = auth::User::Deserialize(ParseJson(it->second));
|
||||
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) {
|
||||
if (auto user = GetUser(name)) {
|
||||
if (!user->db_access().SetDefault(db)) {
|
||||
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
|
||||
Auth::Result Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
|
||||
using enum Auth::Result;
|
||||
if (module_.IsUsed()) {
|
||||
if (auto role = GetRole(name)) {
|
||||
SetMainDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
SaveUser(*user, system_tx);
|
||||
return true;
|
||||
return NO_ROLE;
|
||||
}
|
||||
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
|
||||
|
||||
|
@ -29,6 +29,18 @@ using SynchedAuth = memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph
|
||||
|
||||
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.
|
||||
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
||||
@ -61,6 +73,25 @@ class Auth final {
|
||||
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);
|
||||
|
||||
/**
|
||||
@ -89,7 +120,7 @@ class Auth final {
|
||||
* @return a user when the username and password match, nullopt otherwise
|
||||
* @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.
|
||||
@ -101,6 +132,8 @@ class Auth final {
|
||||
*/
|
||||
std::optional<User> GetUser(const std::string &username) const;
|
||||
|
||||
void LinkUser(User &user) const;
|
||||
|
||||
/**
|
||||
* Saves a user object to the storage.
|
||||
*
|
||||
@ -163,6 +196,13 @@ class Auth final {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -173,6 +213,37 @@ class Auth final {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -229,16 +300,6 @@ class Auth final {
|
||||
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
||||
|
||||
#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.
|
||||
*
|
||||
@ -247,7 +308,33 @@ class Auth final {
|
||||
* @return true on success
|
||||
* @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.
|
||||
@ -265,9 +352,17 @@ class Auth final {
|
||||
* @return true on success
|
||||
* @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
|
||||
|
||||
bool UpToDate(Epoch &e) const {
|
||||
bool res = e == epoch_;
|
||||
e = epoch_;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief
|
||||
@ -278,11 +373,18 @@ class Auth final {
|
||||
*/
|
||||
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,
|
||||
// Auth is not thread-safe because modifying users and roles might require
|
||||
// more than one operation on the storage.
|
||||
kvstore::KVStore storage_;
|
||||
auth::Module module_;
|
||||
Config config_;
|
||||
Epoch epoch_{kStartEpoch};
|
||||
};
|
||||
} // namespace memgraph::auth
|
||||
|
@ -8,10 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json/json.hpp>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <json/json.hpp>
|
||||
|
||||
namespace memgraph::auth {
|
||||
/// Need to be stable, auth durability depends on this
|
||||
enum class PasswordHashAlgorithm : uint8_t { BCRYPT = 0, SHA256 = 1, SHA256_MULTIPLE = 2 };
|
||||
|
@ -425,10 +425,11 @@ Role::Role(const std::string &rolename, const Permissions &permissions)
|
||||
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
|
||||
#ifdef MG_ENTERPRISE
|
||||
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)),
|
||||
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
|
||||
|
||||
const std::string &Role::rolename() const { return rolename_; }
|
||||
@ -454,8 +455,10 @@ nlohmann::json Role::Serialize() const {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
||||
data[kDatabases] = db_access_.Serialize();
|
||||
} else {
|
||||
data[kFineGrainedAccessHandler] = {};
|
||||
data[kDatabases] = {};
|
||||
}
|
||||
#endif
|
||||
return data;
|
||||
@ -471,12 +474,21 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
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;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||
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
|
||||
return {data[kRoleName], permissions};
|
||||
@ -493,7 +505,7 @@ bool operator==(const Role &first, const Role &second) {
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
void Databases::Add(std::string_view db) {
|
||||
void Databases::Grant(std::string_view db) {
|
||||
if (allow_all_) {
|
||||
grants_dbs_.clear();
|
||||
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
|
||||
}
|
||||
|
||||
void Databases::Remove(const std::string &db) {
|
||||
void Databases::Deny(const std::string &db) {
|
||||
denies_dbs_.emplace(db);
|
||||
grants_dbs_.erase(db);
|
||||
}
|
||||
|
||||
void Databases::Delete(const std::string &db) {
|
||||
void Databases::Revoke(const std::string &db) {
|
||||
denies_dbs_.erase(db);
|
||||
if (!allow_all_) {
|
||||
grants_dbs_.erase(db);
|
||||
}
|
||||
// Reset if default deleted
|
||||
if (default_db_ == db) {
|
||||
default_db_ = "";
|
||||
if (main_db_ == db) {
|
||||
main_db_ = "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -530,9 +542,16 @@ void Databases::DenyAll() {
|
||||
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;
|
||||
default_db_ = db;
|
||||
main_db_ = db;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -540,11 +559,11 @@ bool Databases::SetDefault(std::string_view db) {
|
||||
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
||||
}
|
||||
|
||||
const std::string &Databases::GetDefault() const {
|
||||
if (!Contains(default_db_)) {
|
||||
throw AuthException("No access to the set default database \"{}\".", default_db_);
|
||||
const std::string &Databases::GetMain() const {
|
||||
if (!Contains(main_db_)) {
|
||||
throw AuthException("No access to the set default database \"{}\".", main_db_);
|
||||
}
|
||||
return default_db_;
|
||||
return main_db_;
|
||||
}
|
||||
|
||||
nlohmann::json Databases::Serialize() const {
|
||||
@ -552,7 +571,7 @@ nlohmann::json Databases::Serialize() const {
|
||||
data[kGrants] = grants_dbs_;
|
||||
data[kDenies] = denies_dbs_;
|
||||
data[kAllowAll] = allow_all_;
|
||||
data[kDefault] = default_db_;
|
||||
data[kDefault] = main_db_;
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -719,15 +738,16 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
} else {
|
||||
// Back-compatibility
|
||||
spdlog::warn("User without specified database access. Given access to the default database.");
|
||||
db_access.Add(dbms::kDefaultDB);
|
||||
db_access.SetDefault(dbms::kDefaultDB);
|
||||
db_access.Grant(dbms::kDefaultDB);
|
||||
db_access.SetMain(dbms::kDefaultDB);
|
||||
}
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||
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
|
||||
return {data[kUsername], std::move(password_hash), permissions};
|
||||
|
@ -205,52 +205,10 @@ class FineGrainedAccessHandler final {
|
||||
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
|
||||
#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
|
||||
class Databases final {
|
||||
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 &operator=(const Databases &) = default;
|
||||
@ -263,7 +221,7 @@ class Databases final {
|
||||
*
|
||||
* @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.
|
||||
@ -272,7 +230,7 @@ class Databases final {
|
||||
*
|
||||
* @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.
|
||||
@ -280,7 +238,7 @@ class Databases final {
|
||||
*
|
||||
* @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.
|
||||
@ -292,10 +250,15 @@ class Databases final {
|
||||
*/
|
||||
void DenyAll();
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to false and clears grants and denied sets.
|
||||
*/
|
||||
void RevokeAll();
|
||||
|
||||
/**
|
||||
* @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.
|
||||
@ -304,11 +267,13 @@ class Databases final {
|
||||
* @return true if allow_all and not denied or granted
|
||||
*/
|
||||
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_; }
|
||||
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::string &GetDefault() const;
|
||||
const std::string &GetMain() const;
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
@ -320,15 +285,69 @@ class Databases final {
|
||||
: grants_dbs_(std::move(grant)),
|
||||
denies_dbs_(std::move(deny)),
|
||||
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<>> denies_dbs_; //!< set of databases with denied access
|
||||
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
|
||||
|
||||
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.
|
||||
class User final {
|
||||
public:
|
||||
@ -388,6 +407,18 @@ class User final {
|
||||
#ifdef MG_ENTERPRISE
|
||||
Databases &db_access() { 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
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
@ -403,7 +434,7 @@ class User final {
|
||||
Permissions permissions_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||
Databases database_access_;
|
||||
Databases database_access_{};
|
||||
#endif
|
||||
std::optional<Role> role_;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// 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
|
||||
@ -403,7 +403,7 @@ nlohmann::json Module::Call(const nlohmann::json ¶ms, int timeout_millisec)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Module::IsUsed() { return !module_executable_path_.empty(); }
|
||||
bool Module::IsUsed() const { return !module_executable_path_.empty(); }
|
||||
|
||||
void Module::Shutdown() {
|
||||
if (pid_ == -1) return;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// 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
|
||||
@ -49,7 +49,7 @@ class Module final {
|
||||
/// specified executable path and can thus be used.
|
||||
///
|
||||
/// @return boolean indicating whether the module can be used
|
||||
bool IsUsed();
|
||||
bool IsUsed() const;
|
||||
|
||||
~Module();
|
||||
|
||||
|
@ -18,11 +18,9 @@
|
||||
#include "utils/enum.hpp"
|
||||
|
||||
namespace memgraph::slk {
|
||||
|
||||
// Serialize code for auth::Role
|
||||
void Save(const auth::Role &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.Serialize().dump(), builder);
|
||||
}
|
||||
void Save(const auth::Role &self, Builder *builder) { memgraph::slk::Save(self.Serialize().dump(), builder); }
|
||||
|
||||
namespace {
|
||||
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
|
||||
std::string tmp;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -15,6 +15,9 @@
|
||||
#include "communication/bolt/v1/value.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
#include "communication/bolt/v1/fmt.hpp"
|
||||
#include "io/network/fmt.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t kBoltV43Version[4] = {0x00, 0x00, 0x03, 0x04};
|
||||
constexpr uint8_t kEmptyBoltVersion[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
|
27
src/communication/bolt/v1/fmt.hpp
Normal file
27
src/communication/bolt/v1/fmt.hpp
Normal 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
20
src/communication/fmt.hpp
Normal 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
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -21,6 +21,7 @@
|
||||
#include <boost/beast/core.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
#include "communication/http/session.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
@ -82,7 +83,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::info("HTTP server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||
spdlog::info("HTTP server is listening on {}", endpoint);
|
||||
}
|
||||
|
||||
void DoAccept() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -23,6 +23,7 @@
|
||||
|
||||
#include "communication/session.hpp"
|
||||
#include "io/network/epoll.hpp"
|
||||
#include "io/network/fmt.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/signals.hpp"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -22,6 +22,7 @@
|
||||
|
||||
#include "communication/init.hpp"
|
||||
#include "communication/listener.hpp"
|
||||
#include "io/network/fmt.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -26,6 +26,7 @@
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
#include "communication/init.hpp"
|
||||
#include "communication/v2/listener.hpp"
|
||||
#include "communication/v2/pool.hpp"
|
||||
@ -129,7 +130,7 @@ bool Server<TSession, TSessionContext>::Start() {
|
||||
listener_->Start();
|
||||
|
||||
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();
|
||||
|
||||
return true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -47,6 +47,7 @@
|
||||
#include "communication/buffer.hpp"
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/exceptions.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
#include "dbms/global.hpp"
|
||||
#include "utils/event_counter.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
@ -212,14 +213,11 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
session_.Execute();
|
||||
DoRead();
|
||||
} catch (const SessionClosedException &e) {
|
||||
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
spdlog::info("{} client {} closed the connection.", service_name_, remote_endpoint_);
|
||||
DoClose();
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error(
|
||||
"Exception was thrown while processing event in {} session "
|
||||
"associated with {}:{}",
|
||||
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||
spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
|
||||
remote_endpoint_);
|
||||
spdlog::debug("Exception message: {}", e.what());
|
||||
DoClose();
|
||||
}
|
||||
@ -376,8 +374,7 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
socket.lowest_layer().non_blocking(false);
|
||||
});
|
||||
timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max());
|
||||
spdlog::info("Accepted a connection from {}: {}:{}", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
spdlog::info("Accepted a connection from {}: {}", service_name_, remote_endpoint_);
|
||||
}
|
||||
|
||||
void DoRead() {
|
||||
@ -437,14 +434,11 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
session_.Execute();
|
||||
DoRead();
|
||||
} catch (const SessionClosedException &e) {
|
||||
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
spdlog::info("{} client {} closed the connection.", service_name_, remote_endpoint_);
|
||||
DoShutdown();
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error(
|
||||
"Exception was thrown while processing event in {} session "
|
||||
"associated with {}:{}",
|
||||
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||
spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
|
||||
remote_endpoint_);
|
||||
spdlog::debug("Exception message: {}", e.what());
|
||||
DoShutdown();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@ -12,19 +12,44 @@
|
||||
#include "communication/websocket/auth.hpp"
|
||||
|
||||
#include <string>
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
namespace memgraph::communication::websocket {
|
||||
|
||||
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 {
|
||||
if (const auto user = auth_->ReadLock()->GetUser(username); user) {
|
||||
return user->GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
||||
bool SafeAuth::HasPermission(const auth::Permission permission) const {
|
||||
auto locked_auth = auth_->ReadLock();
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); }
|
||||
bool SafeAuth::AccessControlled() const { return auth_->ReadLock()->AccessControlled(); }
|
||||
} // namespace memgraph::communication::websocket
|
||||
|
@ -21,9 +21,9 @@ class AuthenticationInterface {
|
||||
public:
|
||||
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 {
|
||||
@ -32,11 +32,13 @@ class SafeAuth : public AuthenticationInterface {
|
||||
|
||||
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:
|
||||
auth::SynchedAuth *auth_;
|
||||
mutable std::optional<auth::UserOrRole> user_or_role_;
|
||||
mutable auth::Auth::Epoch auth_epoch_{};
|
||||
};
|
||||
} // namespace memgraph::communication::websocket
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// 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
|
||||
@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "communication/websocket/listener.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
|
||||
namespace memgraph::communication::websocket {
|
||||
namespace {
|
||||
@ -61,7 +62,7 @@ Listener::Listener(boost::asio::io_context &ioc, ServerContext *context, tcp::en
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::info("WebSocket server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||
spdlog::info("WebSocket server is listening on {}", endpoint);
|
||||
}
|
||||
|
||||
void Listener::DoAccept() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -80,7 +80,7 @@ bool Session::Run() {
|
||||
return false;
|
||||
}
|
||||
|
||||
authenticated_ = !auth_.HasAnyUsers();
|
||||
authenticated_ = !auth_.AccessControlled();
|
||||
connected_.store(true, std::memory_order_relaxed);
|
||||
|
||||
// run on the strand
|
||||
@ -162,7 +162,7 @@ utils::BasicResult<std::string> Session::Authorize(const nlohmann::json &creds)
|
||||
return {"Authentication failed!"};
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (!auth_.HasUserPermission(creds.at("username").get<std::string>(), auth::Permission::WEBSOCKET)) {
|
||||
if (!auth_.HasPermission(auth::Permission::WEBSOCKET)) {
|
||||
return {"Authorization failed!"};
|
||||
}
|
||||
#endif
|
||||
|
@ -11,7 +11,6 @@ target_sources(mg-coordination
|
||||
include/coordination/coordinator_slk.hpp
|
||||
include/coordination/coordinator_instance.hpp
|
||||
include/coordination/coordinator_handlers.hpp
|
||||
include/coordination/constants.hpp
|
||||
include/coordination/instance_status.hpp
|
||||
include/coordination/replication_instance.hpp
|
||||
include/coordination/raft_state.hpp
|
||||
|
@ -132,7 +132,7 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
|
||||
|
||||
// registering replicas
|
||||
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()) {
|
||||
using enum memgraph::replication::RegisterReplicaError;
|
||||
switch (instance_client.GetError()) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "coordination/fmt.hpp"
|
||||
#include "nuraft/coordinator_state_machine.hpp"
|
||||
#include "nuraft/coordinator_state_manager.hpp"
|
||||
#include "utils/counter.hpp"
|
||||
@ -186,11 +187,9 @@ auto CoordinatorInstance::TryFailover() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: (andi) fmap compliant
|
||||
ReplicationClientsInfo repl_clients_info;
|
||||
repl_clients_info.reserve(repl_instances_.size() - 1);
|
||||
std::ranges::transform(repl_instances_ | ranges::views::filter(is_not_new_main),
|
||||
std::back_inserter(repl_clients_info), &ReplicationInstance::ReplicationClientInfo);
|
||||
auto repl_clients_info = repl_instances_ | ranges::views::filter(is_not_new_main) |
|
||||
ranges::views::transform(&ReplicationInstance::ReplicationClientInfo) |
|
||||
ranges::to<ReplicationClientsInfo>();
|
||||
|
||||
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!");
|
||||
|
60
src/coordination/fmt.hpp
Normal file
60
src/coordination/fmt.hpp
Normal 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
|
@ -19,6 +19,7 @@
|
||||
#include "storage/v2/durability/durability.hpp"
|
||||
#include "storage/v2/durability/snapshot.hpp"
|
||||
#include "storage/v2/durability/version.hpp"
|
||||
#include "storage/v2/fmt.hpp"
|
||||
#include "storage/v2/indices/label_index_stats.hpp"
|
||||
#include "storage/v2/inmemory/storage.hpp"
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
|
@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
@ -19,13 +19,14 @@
|
||||
// Bolt server flags.
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
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;
|
||||
|
||||
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 & {
|
||||
static auto instance = Experiments{};
|
||||
|
@ -23,6 +23,7 @@ namespace memgraph::flags {
|
||||
// old experiments can be reused once code cleanup has happened
|
||||
enum class Experiments : uint8_t {
|
||||
SYSTEM_REPLICATION = 1 << 0,
|
||||
HIGH_AVAILABILITY = 1 << 1,
|
||||
};
|
||||
|
||||
bool AreExperimentsEnabled(Experiments experiments);
|
||||
|
@ -6,5 +6,6 @@ target_sources(mg-glue PRIVATE auth.cpp
|
||||
SessionHL.cpp
|
||||
ServerT.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)
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include "auth/auth.hpp"
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
#include "audit/log.hpp"
|
||||
@ -19,17 +20,22 @@
|
||||
#include "glue/SessionHL.hpp"
|
||||
#include "glue/auth_checker.hpp"
|
||||
#include "glue/communication.hpp"
|
||||
#include "glue/query_user.hpp"
|
||||
#include "glue/run_id.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/discard_value_stream.hpp"
|
||||
#include "query/interpreter_context.hpp"
|
||||
#include "query/query_user.hpp"
|
||||
#include "utils/event_map.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
namespace memgraph::metrics {
|
||||
extern const Event ActiveBoltSessions;
|
||||
} // namespace memgraph::metrics
|
||||
|
||||
namespace {
|
||||
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
|
||||
auto const &as_map = extra.ValueMap();
|
||||
|
||||
@ -97,20 +103,24 @@ std::vector<memgraph::communication::bolt::Value> TypedValueResultStreamBase::De
|
||||
}
|
||||
return decoded_values;
|
||||
}
|
||||
|
||||
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::storage::Storage *storage) : storage_(storage) {}
|
||||
|
||||
namespace memgraph::glue {
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
inline static void MultiDatabaseAuth(const std::optional<auth::User> &user, std::string_view db) {
|
||||
if (user && !AuthChecker::IsUserAuthorized(*user, {}, std::string(db))) {
|
||||
void MultiDatabaseAuth(memgraph::query::QueryUserOrRole *user, std::string_view db) {
|
||||
if (user && !user->IsAuthorized({}, std::string(db), &memgraph::query::session_long_policy)) {
|
||||
throw memgraph::communication::bolt::ClientError(
|
||||
"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() {
|
||||
if (user_.has_value()) {
|
||||
return user_->db_access().GetDefault();
|
||||
if (user_or_role_) {
|
||||
return user_or_role_->GetDefaultDB();
|
||||
}
|
||||
return std::string{memgraph::dbms::kDefaultDB};
|
||||
}
|
||||
@ -132,13 +142,18 @@ bool SessionHL::Authenticate(const std::string &username, const std::string &pas
|
||||
interpreter_.ResetUser();
|
||||
{
|
||||
auto locked_auth = auth_->Lock();
|
||||
if (locked_auth->HasUsers()) {
|
||||
user_ = locked_auth->Authenticate(username, password);
|
||||
if (user_.has_value()) {
|
||||
interpreter_.SetUser(user_->username());
|
||||
if (locked_auth->AccessControlled()) {
|
||||
const auto user_or_role = locked_auth->Authenticate(username, password);
|
||||
if (user_or_role.has_value()) {
|
||||
user_or_role_ = AuthChecker::GenQueryUser(auth_, *user_or_role);
|
||||
interpreter_.SetUser(AuthChecker::GenQueryUser(auth_, *user_or_role));
|
||||
} else {
|
||||
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
|
||||
@ -195,21 +210,17 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
const std::string *username{nullptr};
|
||||
if (user_) {
|
||||
username = &user_->username();
|
||||
}
|
||||
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
auto &db = interpreter_.current_db_.db_acc_;
|
||||
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
||||
memgraph::storage::PropertyValue(params_pv), db ? db->get()->name() : "no known database");
|
||||
const auto username = user_or_role_ ? (user_or_role_->username() ? *user_or_role_->username() : "") : "";
|
||||
audit_log_->Record(endpoint_.address().to_string(), username, query, memgraph::storage::PropertyValue(params_pv),
|
||||
db ? db->get()->name() : "no known database");
|
||||
}
|
||||
#endif
|
||||
try {
|
||||
auto result = interpreter_.Prepare(query, params_pv, ToQueryExtras(extra));
|
||||
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();
|
||||
if (db_name.empty()) {
|
||||
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
|
||||
if (update) {
|
||||
MultiDatabaseAuth(user_, db);
|
||||
MultiDatabaseAuth(user_or_role_.get(), db);
|
||||
interpreter_.SetCurrentDB(db, in_explicit_db_);
|
||||
}
|
||||
#endif
|
||||
@ -338,7 +349,7 @@ SessionHL::SessionHL(memgraph::query::InterpreterContext *interpreter_context,
|
||||
// Metrics update
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
|
||||
#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
|
||||
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); });
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "communication/v2/server.hpp"
|
||||
#include "communication/v2/session.hpp"
|
||||
#include "dbms/database.hpp"
|
||||
#include "glue/query_user.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
|
||||
namespace memgraph::glue {
|
||||
@ -82,7 +83,7 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
|
||||
|
||||
memgraph::query::InterpreterContext *interpreter_context_;
|
||||
memgraph::query::Interpreter interpreter_;
|
||||
std::optional<memgraph::auth::User> user_;
|
||||
std::unique_ptr<query::QueryUserOrRole> user_or_role_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::audit::Log *audit_log_;
|
||||
bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata
|
||||
|
@ -14,53 +14,74 @@
|
||||
#include "auth/auth.hpp"
|
||||
#include "auth/models.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "glue/query_user.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/constants.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/query_user.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
namespace {
|
||||
bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba,
|
||||
const std::vector<memgraph::storage::LabelId> &labels,
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||
bool IsAuthorizedLabels(const memgraph::auth::UserOrRole &user_or_role, const memgraph::query::DbAccessor *dba,
|
||||
const std::vector<memgraph::storage::LabelId> &labels,
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return std::all_of(labels.begin(), labels.end(), [dba, &user, fine_grained_privilege](const auto &label) {
|
||||
return user.GetFineGrainedAccessLabelPermissions().Has(
|
||||
dba->LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
|
||||
fine_grained_privilege)) == memgraph::auth::PermissionLevel::GRANT;
|
||||
return std::all_of(labels.begin(), labels.end(), [dba, &user_or_role, fine_grained_privilege](const auto &label) {
|
||||
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||
return user_or_role.GetFineGrainedAccessLabelPermissions().Has(
|
||||
dba->LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
|
||||
fine_grained_privilege)) ==
|
||||
memgraph::auth::PermissionLevel::GRANT;
|
||||
}},
|
||||
user_or_role);
|
||||
});
|
||||
}
|
||||
|
||||
bool IsUserAuthorizedGloballyLabels(const memgraph::auth::User &user,
|
||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||
bool IsAuthorizedGloballyLabels(const memgraph::auth::UserOrRole &user_or_role,
|
||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return user.GetFineGrainedAccessLabelPermissions().Has(memgraph::query::kAsterisk, fine_grained_permission) ==
|
||||
memgraph::auth::PermissionLevel::GRANT;
|
||||
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||
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,
|
||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||
bool IsAuthorizedGloballyEdges(const memgraph::auth::UserOrRole &user_or_role,
|
||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return user.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::query::kAsterisk, fine_grained_permission) ==
|
||||
memgraph::auth::PermissionLevel::GRANT;
|
||||
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||
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,
|
||||
const memgraph::storage::EdgeTypeId &edgeType,
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||
bool IsAuthorizedEdgeType(const memgraph::auth::UserOrRole &user_or_role, const memgraph::query::DbAccessor *dba,
|
||||
const memgraph::storage::EdgeTypeId &edgeType,
|
||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return user.GetFineGrainedAccessEdgeTypePermissions().Has(
|
||||
dba->EdgeTypeToName(edgeType), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
|
||||
fine_grained_privilege)) == memgraph::auth::PermissionLevel::GRANT;
|
||||
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||
return user_or_role.GetFineGrainedAccessEdgeTypePermissions().Has(
|
||||
dba->EdgeTypeToName(edgeType),
|
||||
memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege)) ==
|
||||
memgraph::auth::PermissionLevel::GRANT;
|
||||
}},
|
||||
user_or_role);
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
@ -68,47 +89,54 @@ namespace memgraph::glue {
|
||||
|
||||
AuthChecker::AuthChecker(memgraph::auth::SynchedAuth *auth) : auth_(auth) {}
|
||||
|
||||
bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) const {
|
||||
std::optional<memgraph::auth::User> maybe_user;
|
||||
{
|
||||
auto locked_auth = auth_->ReadLock();
|
||||
if (!locked_auth->HasUsers()) {
|
||||
return true;
|
||||
}
|
||||
if (username.has_value()) {
|
||||
maybe_user = locked_auth->GetUser(*username);
|
||||
}
|
||||
std::shared_ptr<query::QueryUserOrRole> AuthChecker::GenQueryUser(const std::optional<std::string> &username,
|
||||
const std::optional<std::string> &rolename) const {
|
||||
const auto user_or_role = auth_->ReadLock()->GetUserOrRole(username, rolename);
|
||||
if (user_or_role) {
|
||||
return std::make_shared<QueryUserOrRole>(auth_, *user_or_role);
|
||||
}
|
||||
// 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
|
||||
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()) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
auto user = user_.Lock();
|
||||
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());
|
||||
if (!user_or_role || !*user_or_role) {
|
||||
throw query::QueryRuntimeException("No user specified for fine grained authorization!");
|
||||
}
|
||||
}
|
||||
|
||||
void AuthChecker::ClearCache() const {
|
||||
user_.WithLock([](auto &user) mutable { user = {}; });
|
||||
// Convert from query user to auth user or role
|
||||
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
|
||||
|
||||
@ -116,7 +144,7 @@ bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
|
||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) { // NOLINT
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (!db_name.empty() && !user.db_access().Contains(db_name)) {
|
||||
if (!db_name.empty() && !user.HasAccess(db_name)) {
|
||||
return false;
|
||||
}
|
||||
#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
|
||||
FineGrainedAuthChecker::FineGrainedAuthChecker(auth::User user, const memgraph::query::DbAccessor *dba)
|
||||
: user_{std::move(user)}, dba_(dba){};
|
||||
if (!db_name.empty() && !role.HasAccess(db_name)) {
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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(
|
||||
@ -170,7 +223,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return IsUserAuthorizedGloballyLabels(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||
return IsAuthorizedGloballyLabels(user_or_role_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||
}
|
||||
|
||||
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
|
||||
@ -178,7 +231,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
|
||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
return true;
|
||||
}
|
||||
return IsUserAuthorizedGloballyEdges(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||
return IsAuthorizedGloballyEdges(user_or_role_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||
};
|
||||
#endif
|
||||
} // namespace memgraph::glue
|
||||
|
@ -22,53 +22,59 @@ namespace memgraph::glue {
|
||||
|
||||
class AuthChecker : public query::AuthChecker {
|
||||
public:
|
||||
explicit AuthChecker(memgraph::auth::SynchedAuth *auth);
|
||||
explicit AuthChecker(auth::SynchedAuth *auth);
|
||||
|
||||
bool IsUserAuthorized(const std::optional<std::string> &username,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) const override;
|
||||
std::shared_ptr<query::QueryUserOrRole> GenQueryUser(const std::optional<std::string> &username,
|
||||
const std::optional<std::string> &rolename) const override;
|
||||
|
||||
static std::unique_ptr<query::QueryUserOrRole> GenQueryUser(auth::SynchedAuth *auth,
|
||||
const std::optional<auth::UserOrRole> &user_or_role);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
||||
const std::string &username, const memgraph::query::DbAccessor *dba) const override;
|
||||
|
||||
void ClearCache() const override;
|
||||
|
||||
std::unique_ptr<query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(std::shared_ptr<query::QueryUserOrRole> user,
|
||||
const query::DbAccessor *dba) const override;
|
||||
#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 = "");
|
||||
|
||||
[[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:
|
||||
memgraph::auth::SynchedAuth *auth_;
|
||||
mutable memgraph::utils::Synchronized<auth::User, memgraph::utils::SpinLock> user_; // cached user
|
||||
auth::SynchedAuth *auth_;
|
||||
mutable utils::Synchronized<auth::UserOrRole, utils::SpinLock> user_or_role_; // cached user
|
||||
};
|
||||
#ifdef MG_ENTERPRISE
|
||||
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
|
||||
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;
|
||||
|
||||
bool Has(const query::EdgeAccessor &edge,
|
||||
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;
|
||||
|
||||
bool Has(const memgraph::storage::EdgeTypeId &edge_type,
|
||||
bool Has(const storage::EdgeTypeId &edge_type,
|
||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||
|
||||
bool HasGlobalPrivilegeOnVertices(
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||
bool HasGlobalPrivilegeOnVertices(query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||
|
||||
bool HasGlobalPrivilegeOnEdges(
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||
bool HasGlobalPrivilegeOnEdges(query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||
|
||||
private:
|
||||
auth::User user_;
|
||||
const memgraph::query::DbAccessor *dba_;
|
||||
auth::UserOrRole user_or_role_;
|
||||
const query::DbAccessor *dba_;
|
||||
};
|
||||
#endif
|
||||
} // namespace memgraph::glue
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "auth/models.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
@ -123,6 +124,29 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowRolePrivileges(
|
||||
}
|
||||
|
||||
#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(
|
||||
const std::optional<memgraph::auth::User> &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 &allows = db.GetAllowAll();
|
||||
const auto &grants = db.GetGrants();
|
||||
const auto &denies = db.GetDenies();
|
||||
auto allows = db.GetAllowAll();
|
||||
auto grants = db.GetGrants();
|
||||
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
|
||||
if (allows) {
|
||||
@ -287,7 +317,7 @@ bool AuthQueryHandler::CreateUser(const std::string &username, const std::option
|
||||
,
|
||||
system_tx);
|
||||
#ifdef MG_ENTERPRISE
|
||||
GrantDatabaseToUser(auth::kAllDatabases, username, system_tx);
|
||||
GrantDatabase(auth::kAllDatabases, username, system_tx);
|
||||
SetMainDatabase(dbms::kDefaultDB, username, system_tx);
|
||||
#endif
|
||||
}
|
||||
@ -334,51 +364,97 @@ bool AuthQueryHandler::CreateRole(const std::string &rolename, system::Transacti
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
bool AuthQueryHandler::RevokeDatabaseFromUser(const std::string &db_name, const std::string &username,
|
||||
system::Transaction *system_tx) {
|
||||
void AuthQueryHandler::GrantDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||
system::Transaction *system_tx) {
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) return false;
|
||||
return locked_auth->RevokeDatabaseFromUser(db_name, username, system_tx);
|
||||
const auto res = locked_auth->GrantDatabase(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) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthQueryHandler::GrantDatabaseToUser(const std::string &db_name, const std::string &username,
|
||||
system::Transaction *system_tx) {
|
||||
void AuthQueryHandler::DenyDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||
system::Transaction *system_tx) {
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) return false;
|
||||
return locked_auth->GrantDatabaseToUser(db_name, username, system_tx);
|
||||
const auto res = locked_auth->DenyDatabase(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) {
|
||||
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) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges(
|
||||
const std::string &username) {
|
||||
const std::string &user_or_role) {
|
||||
try {
|
||||
auto locked_auth = auth_->ReadLock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist.", username);
|
||||
if (auto user = locked_auth->GetUser(user_or_role)) {
|
||||
return ShowDatabasePrivileges(user);
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
try {
|
||||
auto locked_auth = auth_->Lock();
|
||||
auto user = locked_auth->GetUser(username);
|
||||
if (!user) return false;
|
||||
return locked_auth->SetMainDatabase(db_name, username, system_tx);
|
||||
const auto res = locked_auth->SetMainDatabase(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) {
|
||||
throw memgraph::query::QueryRuntimeException(e.what());
|
||||
}
|
||||
|
@ -37,15 +37,19 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
||||
system::Transaction *system_tx) override;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
bool RevokeDatabaseFromUser(const std::string &db_name, const std::string &username,
|
||||
system::Transaction *system_tx) override;
|
||||
void GrantDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||
system::Transaction *system_tx) override;
|
||||
|
||||
bool GrantDatabaseToUser(const std::string &db_name, const std::string &username,
|
||||
system::Transaction *system_tx) override;
|
||||
void DenyDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||
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;
|
||||
#endif
|
||||
|
41
src/glue/query_user.cpp
Normal file
41
src/glue/query_user.cpp
Normal 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
57
src/glue/query_user.hpp
Normal 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
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -22,6 +22,7 @@
|
||||
|
||||
#include "integrations/constants.hpp"
|
||||
#include "integrations/kafka/exceptions.hpp"
|
||||
#include "integrations/kafka/fmt.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
|
25
src/integrations/kafka/fmt.hpp
Normal file
25
src/integrations/kafka/fmt.hpp
Normal 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
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -15,12 +15,12 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <pulsar/Client.h>
|
||||
#include <pulsar/InitialPosition.h>
|
||||
|
||||
#include "integrations/constants.hpp"
|
||||
#include "integrations/pulsar/exceptions.hpp"
|
||||
#include "integrations/pulsar/fmt.hpp"
|
||||
#include "utils/concepts.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
|
21
src/integrations/pulsar/fmt.hpp
Normal file
21
src/integrations/pulsar/fmt.hpp
Normal 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
|
@ -11,12 +11,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace memgraph::coordination {
|
||||
#if FMT_VERSION > 90000
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#ifdef MG_EXPERIMENTAL_HIGH_AVAILABILITY
|
||||
constexpr bool allow_ha = true;
|
||||
#else
|
||||
constexpr bool allow_ha = false;
|
||||
#include "io/network/endpoint.hpp"
|
||||
|
||||
template <>
|
||||
class fmt::formatter<memgraph::io::network::Endpoint> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::coordination
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace memgraph::io::network {
|
||||
|
@ -160,13 +160,14 @@ class KVStore final {
|
||||
* and behaves as if all of those pairs are stored in a single iterable
|
||||
* collection of std::pair<std::string, std::string>.
|
||||
*/
|
||||
class iterator final : public std::iterator<std::input_iterator_tag, // iterator_category
|
||||
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
|
||||
> {
|
||||
class iterator final {
|
||||
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);
|
||||
|
||||
iterator(const iterator &other) = delete;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "helpers.hpp"
|
||||
#include "license/license_sender.hpp"
|
||||
#include "memory/global_memory_control.hpp"
|
||||
#include "query/auth_checker.hpp"
|
||||
#include "query/auth_query_handler.hpp"
|
||||
#include "query/config.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,
|
||||
std::string cypherl_file_path, memgraph::audit::Log *audit_log = nullptr) {
|
||||
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()) {
|
||||
spdlog::trace("Could not find init file {}", cypherl_file_path);
|
||||
return;
|
||||
|
@ -139,6 +139,11 @@ struct NodeId {
|
||||
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; }
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const NodeId &node_id) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@ -274,3 +274,8 @@ inline void RestoreError(ExceptionInfo exc_info) {
|
||||
}
|
||||
|
||||
} // namespace memgraph::py
|
||||
|
||||
#if FMT_VERSION > 90000
|
||||
template <>
|
||||
class fmt::formatter<memgraph::py::ExceptionInfo> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
|
@ -40,6 +40,7 @@ set(mg_query_sources
|
||||
db_accessor.cpp
|
||||
auth_query_handler.cpp
|
||||
interpreter_context.cpp
|
||||
query_user.cpp
|
||||
)
|
||||
|
||||
add_library(mg-query STATIC ${mg_query_sources})
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// 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
|
||||
@ -16,7 +16,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "dbms/constants.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/query_user.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
|
||||
namespace memgraph::query {
|
||||
@ -29,15 +31,12 @@ class AuthChecker {
|
||||
public:
|
||||
virtual ~AuthChecker() = default;
|
||||
|
||||
[[nodiscard]] virtual bool IsUserAuthorized(const std::optional<std::string> &username,
|
||||
const std::vector<AuthQuery::Privilege> &privileges,
|
||||
const std::string &db_name) const = 0;
|
||||
virtual std::shared_ptr<QueryUserOrRole> GenQueryUser(const std::optional<std::string> &username,
|
||||
const std::optional<std::string> &rolename) const = 0;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
[[nodiscard]] virtual std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
||||
const std::string &username, const DbAccessor *db_accessor) const = 0;
|
||||
|
||||
virtual void ClearCache() const = 0;
|
||||
std::shared_ptr<QueryUserOrRole> user, const DbAccessor *db_accessor) const = 0;
|
||||
#endif
|
||||
};
|
||||
#ifdef MG_ENTERPRISE
|
||||
@ -98,19 +97,29 @@ class AllowEverythingFineGrainedAuthChecker final : public FineGrainedAuthChecke
|
||||
|
||||
class AllowEverythingAuthChecker final : public AuthChecker {
|
||||
public:
|
||||
bool IsUserAuthorized(const std::optional<std::string> & /*username*/,
|
||||
const std::vector<AuthQuery::Privilege> & /*privileges*/,
|
||||
const std::string & /*db*/) const override {
|
||||
return true;
|
||||
struct User : query::QueryUserOrRole {
|
||||
User() : query::QueryUserOrRole{std::nullopt, std::nullopt} {}
|
||||
User(std::string name) : query::QueryUserOrRole{std::move(name), std::nullopt} {}
|
||||
bool IsAuthorized(const std::vector<AuthQuery::Privilege> & /*privileges*/, const std::string & /*db_name*/,
|
||||
UserPolicy * /*policy*/) const override {
|
||||
return true;
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::string GetDefaultDB() const override { return std::string{dbms::kDefaultDB}; }
|
||||
#endif
|
||||
};
|
||||
|
||||
std::shared_ptr<query::QueryUserOrRole> GenQueryUser(const std::optional<std::string> &name,
|
||||
const std::optional<std::string> & /*role*/) const override {
|
||||
if (name) return std::make_shared<User>(std::move(*name));
|
||||
return std::make_shared<User>();
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(const std::string & /*username*/,
|
||||
std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(std::shared_ptr<QueryUserOrRole> /*user*/,
|
||||
const DbAccessor * /*dba*/) const override {
|
||||
return std::make_unique<AllowEverythingFineGrainedAuthChecker>();
|
||||
}
|
||||
|
||||
void ClearCache() const override {}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -46,15 +46,17 @@ class AuthQueryHandler {
|
||||
system::Transaction *system_tx) = 0;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/// Return true if access revoked successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool RevokeDatabaseFromUser(const std::string &db, const std::string &username,
|
||||
system::Transaction *system_tx) = 0;
|
||||
|
||||
/// Return true if access granted successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool GrantDatabaseToUser(const std::string &db, const std::string &username,
|
||||
system::Transaction *system_tx) = 0;
|
||||
virtual void GrantDatabase(const std::string &db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||
|
||||
/// Return true if access revoked successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void DenyDatabase(const std::string &db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||
|
||||
/// Return true if access revoked successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void RevokeDatabase(const std::string &db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||
|
||||
/// Returns database access rights for the user
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
@ -62,7 +64,7 @@ class AuthQueryHandler {
|
||||
|
||||
/// Return true if main database set successfully
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool SetMainDatabase(std::string_view db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||
virtual void SetMainDatabase(std::string_view db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||
|
||||
/// Delete database from all users
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/fmt.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/semantic/symbol.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
|
23
src/query/fmt.hpp
Normal file
23
src/query/fmt.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
// 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 "query/typed_value.hpp"
|
||||
|
||||
template <>
|
||||
class fmt::formatter<memgraph::query::TypedValue> : public fmt::ostream_formatter {};
|
||||
template <>
|
||||
class fmt::formatter<memgraph::query::TypedValue::Type> : public fmt::ostream_formatter {};
|
||||
#endif
|
@ -2819,6 +2819,7 @@ class AuthQuery : public memgraph::query::Query {
|
||||
SHOW_ROLE_FOR_USER,
|
||||
SHOW_USERS_FOR_ROLE,
|
||||
GRANT_DATABASE_TO_USER,
|
||||
DENY_DATABASE_FROM_USER,
|
||||
REVOKE_DATABASE_FROM_USER,
|
||||
SHOW_DATABASE_PRIVILEGES,
|
||||
SET_MAIN_DATABASE,
|
||||
|
@ -1780,22 +1780,35 @@ antlrcpp::Any CypherMainVisitor::visitShowUsersForRole(MemgraphCypher::ShowUsers
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitGrantDatabaseToUser(MemgraphCypher::GrantDatabaseToUserContext *ctx) {
|
||||
antlrcpp::Any CypherMainVisitor::visitGrantDatabaseToUserOrRole(MemgraphCypher::GrantDatabaseToUserOrRoleContext *ctx) {
|
||||
auto *auth = storage_->Create<AuthQuery>();
|
||||
auth->action_ = AuthQuery::Action::GRANT_DATABASE_TO_USER;
|
||||
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||
return auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUser(MemgraphCypher::RevokeDatabaseFromUserContext *ctx) {
|
||||
antlrcpp::Any CypherMainVisitor::visitDenyDatabaseFromUserOrRole(
|
||||
MemgraphCypher::DenyDatabaseFromUserOrRoleContext *ctx) {
|
||||
auto *auth = storage_->Create<AuthQuery>();
|
||||
auth->action_ = AuthQuery::Action::DENY_DATABASE_FROM_USER;
|
||||
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||
return auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUserOrRole(
|
||||
MemgraphCypher::RevokeDatabaseFromUserOrRoleContext *ctx) {
|
||||
auto *auth = storage_->Create<AuthQuery>();
|
||||
auth->action_ = AuthQuery::Action::REVOKE_DATABASE_FROM_USER;
|
||||
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||
return auth;
|
||||
}
|
||||
|
||||
@ -1805,7 +1818,7 @@ antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUser(MemgraphCypher::Rev
|
||||
antlrcpp::Any CypherMainVisitor::visitShowDatabasePrivileges(MemgraphCypher::ShowDatabasePrivilegesContext *ctx) {
|
||||
auto *auth = storage_->Create<AuthQuery>();
|
||||
auth->action_ = AuthQuery::Action::SHOW_DATABASE_PRIVILEGES;
|
||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||
return auth;
|
||||
}
|
||||
|
||||
@ -1816,7 +1829,7 @@ antlrcpp::Any CypherMainVisitor::visitSetMainDatabase(MemgraphCypher::SetMainDat
|
||||
auto *auth = storage_->Create<AuthQuery>();
|
||||
auth->action_ = AuthQuery::Action::SET_MAIN_DATABASE;
|
||||
auth->database_ = std::any_cast<std::string>(ctx->db->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
||||
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||
return auth;
|
||||
}
|
||||
|
||||
|
@ -605,12 +605,17 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
antlrcpp::Any visitGrantDatabaseToUser(MemgraphCypher::GrantDatabaseToUserContext *ctx) override;
|
||||
antlrcpp::Any visitGrantDatabaseToUserOrRole(MemgraphCypher::GrantDatabaseToUserOrRoleContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
antlrcpp::Any visitRevokeDatabaseFromUser(MemgraphCypher::RevokeDatabaseFromUserContext *ctx) override;
|
||||
antlrcpp::Any visitDenyDatabaseFromUserOrRole(MemgraphCypher::DenyDatabaseFromUserOrRoleContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
antlrcpp::Any visitRevokeDatabaseFromUserOrRole(MemgraphCypher::RevokeDatabaseFromUserOrRoleContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
|
@ -176,8 +176,9 @@ authQuery : createRole
|
||||
| showPrivileges
|
||||
| showRoleForUser
|
||||
| showUsersForRole
|
||||
| grantDatabaseToUser
|
||||
| revokeDatabaseFromUser
|
||||
| grantDatabaseToUserOrRole
|
||||
| denyDatabaseFromUserOrRole
|
||||
| revokeDatabaseFromUserOrRole
|
||||
| showDatabasePrivileges
|
||||
| setMainDatabase
|
||||
;
|
||||
@ -303,13 +304,15 @@ denyPrivilege : DENY ( ALL PRIVILEGES | privileges=privilegesList ) TO userOrRol
|
||||
|
||||
revokePrivilege : REVOKE ( ALL PRIVILEGES | privileges=revokePrivilegesList ) FROM userOrRole=userOrRoleName ;
|
||||
|
||||
grantDatabaseToUser : GRANT DATABASE db=wildcardName TO user=symbolicName ;
|
||||
grantDatabaseToUserOrRole : GRANT DATABASE db=wildcardName TO userOrRole=userOrRoleName ;
|
||||
|
||||
revokeDatabaseFromUser : REVOKE DATABASE db=wildcardName FROM user=symbolicName ;
|
||||
denyDatabaseFromUserOrRole : DENY DATABASE db=wildcardName FROM userOrRole=userOrRoleName ;
|
||||
|
||||
showDatabasePrivileges : SHOW DATABASE PRIVILEGES FOR user=symbolicName ;
|
||||
revokeDatabaseFromUserOrRole : REVOKE DATABASE db=wildcardName FROM userOrRole=userOrRoleName ;
|
||||
|
||||
setMainDatabase : SET MAIN DATABASE db=symbolicName FOR user=symbolicName ;
|
||||
showDatabasePrivileges : SHOW DATABASE PRIVILEGES FOR userOrRole=userOrRoleName ;
|
||||
|
||||
setMainDatabase : SET MAIN DATABASE db=symbolicName FOR userOrRole=userOrRoleName ;
|
||||
|
||||
privilege : CREATE
|
||||
| DELETE
|
||||
|
@ -68,6 +68,7 @@
|
||||
#include "query/plan/profile.hpp"
|
||||
#include "query/plan/vertex_count_cache.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "query/query_user.hpp"
|
||||
#include "query/replication_query_handler.hpp"
|
||||
#include "query/stream.hpp"
|
||||
#include "query/stream/common.hpp"
|
||||
@ -109,7 +110,6 @@
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "coordination/constants.hpp"
|
||||
#include "flags/experimental.hpp"
|
||||
#endif
|
||||
|
||||
@ -370,7 +370,7 @@ class ReplQueryHandler {
|
||||
.replica_check_frequency = replica_check_frequency,
|
||||
.ssl = std::nullopt};
|
||||
|
||||
const auto error = handler_->TryRegisterReplica(replication_config, true).HasError();
|
||||
const auto error = handler_->TryRegisterReplica(replication_config).HasError();
|
||||
|
||||
if (error) {
|
||||
throw QueryRuntimeException(fmt::format("Couldn't register replica '{}'!", name));
|
||||
@ -630,6 +630,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
||||
AuthQuery::Action::SHOW_USERS_FOR_ROLE,
|
||||
AuthQuery::Action::SHOW_ROLE_FOR_USER,
|
||||
AuthQuery::Action::GRANT_DATABASE_TO_USER,
|
||||
AuthQuery::Action::DENY_DATABASE_FROM_USER,
|
||||
AuthQuery::Action::REVOKE_DATABASE_FROM_USER,
|
||||
AuthQuery::Action::SHOW_DATABASE_PRIVILEGES,
|
||||
AuthQuery::Action::SET_MAIN_DATABASE};
|
||||
@ -889,9 +890,31 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
||||
if (database != memgraph::auth::kAllDatabases) {
|
||||
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||
}
|
||||
if (!auth->GrantDatabaseToUser(database, username, &*interpreter->system_transaction_)) {
|
||||
throw QueryRuntimeException("Failed to grant database {} to user {}.", database, username);
|
||||
auth->GrantDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||
throw QueryRuntimeException(e.what());
|
||||
}
|
||||
#else
|
||||
callback.fn = [] {
|
||||
#endif
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::DENY_DATABASE_FROM_USER:
|
||||
forbid_on_replica();
|
||||
#ifdef MG_ENTERPRISE
|
||||
callback.fn = [auth, database, username, db_handler, interpreter = &interpreter] { // NOLINT
|
||||
if (!interpreter->system_transaction_) {
|
||||
throw QueryException("Expected to be in a system transaction");
|
||||
}
|
||||
|
||||
try {
|
||||
std::optional<memgraph::dbms::DatabaseAccess> db =
|
||||
std::nullopt; // Hold pointer to database to protect it until query is done
|
||||
if (database != memgraph::auth::kAllDatabases) {
|
||||
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||
}
|
||||
auth->DenyDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||
throw QueryRuntimeException(e.what());
|
||||
}
|
||||
@ -915,9 +938,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
||||
if (database != memgraph::auth::kAllDatabases) {
|
||||
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||
}
|
||||
if (!auth->RevokeDatabaseFromUser(database, username, &*interpreter->system_transaction_)) {
|
||||
throw QueryRuntimeException("Failed to revoke database {} from user {}.", database, username);
|
||||
}
|
||||
auth->RevokeDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||
throw QueryRuntimeException(e.what());
|
||||
}
|
||||
@ -950,9 +971,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
||||
try {
|
||||
const auto db =
|
||||
db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||
if (!auth->SetMainDatabase(database, username, &*interpreter->system_transaction_)) {
|
||||
throw QueryRuntimeException("Failed to set main database {} for user {}.", database, username);
|
||||
}
|
||||
auth->SetMainDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||
throw QueryRuntimeException(e.what());
|
||||
}
|
||||
@ -1131,17 +1150,21 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Parameters ¶meters,
|
||||
coordination::CoordinatorState *coordinator_state,
|
||||
const query::InterpreterConfig &config, std::vector<Notification> *notifications) {
|
||||
using enum memgraph::flags::Experiments;
|
||||
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryRuntimeException("High availability is only available in Memgraph Enterprise.");
|
||||
}
|
||||
|
||||
if (!flags::AreExperimentsEnabled(HIGH_AVAILABILITY)) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. If you want to use it, add high-availability option to the "
|
||||
"--experimental-enabled flag.");
|
||||
}
|
||||
|
||||
Callback callback;
|
||||
switch (coordinator_query->action_) {
|
||||
case CoordinatorQuery::Action::ADD_COORDINATOR_INSTANCE: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_raft_server_id) {
|
||||
throw QueryRuntimeException("Only coordinator can add coordinator instance!");
|
||||
}
|
||||
@ -1165,15 +1188,6 @@ Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Param
|
||||
return callback;
|
||||
}
|
||||
case CoordinatorQuery::Action::REGISTER_INSTANCE: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_raft_server_id) {
|
||||
throw QueryRuntimeException("Only coordinator can register coordinator server!");
|
||||
}
|
||||
@ -1205,15 +1219,6 @@ Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Param
|
||||
return callback;
|
||||
}
|
||||
case CoordinatorQuery::Action::UNREGISTER_INSTANCE:
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_raft_server_id) {
|
||||
throw QueryRuntimeException("Only coordinator can register coordinator server!");
|
||||
}
|
||||
@ -1229,14 +1234,6 @@ Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Param
|
||||
return callback;
|
||||
|
||||
case CoordinatorQuery::Action::SET_INSTANCE_TO_MAIN: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_raft_server_id) {
|
||||
throw QueryRuntimeException("Only coordinator can register coordinator server!");
|
||||
}
|
||||
@ -1254,14 +1251,6 @@ Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Param
|
||||
return callback;
|
||||
}
|
||||
case CoordinatorQuery::Action::SHOW_INSTANCES: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_raft_server_id) {
|
||||
throw QueryRuntimeException("Only coordinator can run SHOW INSTANCES.");
|
||||
}
|
||||
@ -1306,7 +1295,7 @@ std::vector<std::string> EvaluateTopicNames(ExpressionVisitor<TypedValue> &evalu
|
||||
Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator,
|
||||
memgraph::dbms::DatabaseAccess db_acc,
|
||||
InterpreterContext *interpreter_context,
|
||||
const std::optional<std::string> &username) {
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||
static constexpr std::string_view kDefaultConsumerGroup = "mg_consumer";
|
||||
std::string consumer_group{stream_query->consumer_group_.empty() ? kDefaultConsumerGroup
|
||||
: stream_query->consumer_group_};
|
||||
@ -1333,10 +1322,13 @@ Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, Exp
|
||||
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::StreamsCreated);
|
||||
|
||||
// Make a copy of the user and pass it to the subsystem
|
||||
auto owner = interpreter_context->auth_checker->GenQueryUser(user_or_role->username(), user_or_role->rolename());
|
||||
|
||||
return [db_acc = std::move(db_acc), interpreter_context, stream_name = stream_query->stream_name_,
|
||||
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
||||
consumer_group = std::move(consumer_group), common_stream_info = std::move(common_stream_info),
|
||||
bootstrap_servers = std::move(bootstrap), owner = username,
|
||||
bootstrap_servers = std::move(bootstrap), owner = std::move(owner),
|
||||
configs = get_config_map(stream_query->configs_, "Configs"),
|
||||
credentials = get_config_map(stream_query->credentials_, "Credentials"),
|
||||
default_server = interpreter_context->config.default_kafka_bootstrap_servers]() mutable {
|
||||
@ -1358,7 +1350,7 @@ Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, Exp
|
||||
Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator,
|
||||
memgraph::dbms::DatabaseAccess db,
|
||||
InterpreterContext *interpreter_context,
|
||||
const std::optional<std::string> &username) {
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||
auto service_url = GetOptionalStringValue(stream_query->service_url_, evaluator);
|
||||
if (service_url && service_url->empty()) {
|
||||
throw SemanticException("Service URL must not be an empty string!");
|
||||
@ -1366,9 +1358,13 @@ Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, Ex
|
||||
auto common_stream_info = GetCommonStreamInfo(stream_query, evaluator);
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::StreamsCreated);
|
||||
|
||||
// Make a copy of the user and pass it to the subsystem
|
||||
auto owner = interpreter_context->auth_checker->GenQueryUser(user_or_role->username(), user_or_role->rolename());
|
||||
|
||||
return [db = std::move(db), interpreter_context, stream_name = stream_query->stream_name_,
|
||||
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
||||
common_stream_info = std::move(common_stream_info), service_url = std::move(service_url), owner = username,
|
||||
common_stream_info = std::move(common_stream_info), service_url = std::move(service_url),
|
||||
owner = std::move(owner),
|
||||
default_service = interpreter_context->config.default_pulsar_service_url]() mutable {
|
||||
std::string url = service_url ? std::move(*service_url) : std::move(default_service);
|
||||
db->streams()->Create<query::stream::PulsarStream>(
|
||||
@ -1382,7 +1378,7 @@ Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, Ex
|
||||
|
||||
Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶meters,
|
||||
memgraph::dbms::DatabaseAccess &db_acc, InterpreterContext *interpreter_context,
|
||||
const std::optional<std::string> &username, std::vector<Notification> *notifications) {
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role, std::vector<Notification> *notifications) {
|
||||
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
||||
// the argument to Callback.
|
||||
EvaluationContext evaluation_context;
|
||||
@ -1395,10 +1391,12 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
||||
case StreamQuery::Action::CREATE_STREAM: {
|
||||
switch (stream_query->type_) {
|
||||
case StreamQuery::Type::KAFKA:
|
||||
callback.fn = GetKafkaCreateCallback(stream_query, evaluator, db_acc, interpreter_context, username);
|
||||
callback.fn =
|
||||
GetKafkaCreateCallback(stream_query, evaluator, db_acc, interpreter_context, std::move(user_or_role));
|
||||
break;
|
||||
case StreamQuery::Type::PULSAR:
|
||||
callback.fn = GetPulsarCreateCallback(stream_query, evaluator, db_acc, interpreter_context, username);
|
||||
callback.fn =
|
||||
GetPulsarCreateCallback(stream_query, evaluator, db_acc, interpreter_context, std::move(user_or_role));
|
||||
break;
|
||||
}
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_STREAM,
|
||||
@ -1671,7 +1669,7 @@ struct TxTimeout {
|
||||
struct PullPlan {
|
||||
explicit PullPlan(std::shared_ptr<PlanWrapper> plan, const Parameters ¶meters, bool is_profile_query,
|
||||
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
||||
std::optional<std::string> username, std::atomic<TransactionStatus> *transaction_status,
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role, std::atomic<TransactionStatus> *transaction_status,
|
||||
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
||||
TriggerContextCollector *trigger_context_collector = nullptr,
|
||||
std::optional<size_t> memory_limit = {}, bool use_monotonic_memory = true,
|
||||
@ -1711,7 +1709,7 @@ struct PullPlan {
|
||||
|
||||
PullPlan::PullPlan(const std::shared_ptr<PlanWrapper> plan, const Parameters ¶meters, const bool is_profile_query,
|
||||
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
||||
std::optional<std::string> username, std::atomic<TransactionStatus> *transaction_status,
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role, std::atomic<TransactionStatus> *transaction_status,
|
||||
std::shared_ptr<utils::AsyncTimer> tx_timer, TriggerContextCollector *trigger_context_collector,
|
||||
const std::optional<size_t> memory_limit, bool use_monotonic_memory,
|
||||
FrameChangeCollector *frame_change_collector)
|
||||
@ -1727,10 +1725,9 @@ PullPlan::PullPlan(const std::shared_ptr<PlanWrapper> plan, const Parameters &pa
|
||||
ctx_.evaluation_context.properties = NamesToProperties(plan->ast_storage().properties_, dba);
|
||||
ctx_.evaluation_context.labels = NamesToLabels(plan->ast_storage().labels_, dba);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && username.has_value() && dba) {
|
||||
// TODO How can we avoid creating this every time? If we must create it, it would be faster with an auth::User
|
||||
// instead of the username
|
||||
auto auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(*username, dba);
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && user_or_role && *user_or_role && dba) {
|
||||
// Create only if an explicit user is defined
|
||||
auto auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(std::move(user_or_role), dba);
|
||||
|
||||
// if the user has global privileges to read, edit and write anything, we don't need to perform authorization
|
||||
// otherwise, we do assign the auth checker to check for label access control
|
||||
@ -2020,7 +2017,7 @@ bool IsCallBatchedProcedureQuery(const std::vector<memgraph::query::Clause *> &c
|
||||
PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string, TypedValue> *summary,
|
||||
InterpreterContext *interpreter_context, CurrentDB ¤t_db,
|
||||
utils::MemoryResource *execution_memory, std::vector<Notification> *notifications,
|
||||
std::optional<std::string> const &username,
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role,
|
||||
std::atomic<TransactionStatus> *transaction_status,
|
||||
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
||||
FrameChangeCollector *frame_change_collector = nullptr) {
|
||||
@ -2088,8 +2085,8 @@ PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string,
|
||||
auto *trigger_context_collector =
|
||||
current_db.trigger_context_collector_ ? &*current_db.trigger_context_collector_ : nullptr;
|
||||
auto pull_plan = std::make_shared<PullPlan>(
|
||||
plan, parsed_query.parameters, false, dba, interpreter_context, execution_memory, username, transaction_status,
|
||||
std::move(tx_timer), trigger_context_collector, memory_limit, use_monotonic_memory,
|
||||
plan, parsed_query.parameters, false, dba, interpreter_context, execution_memory, std::move(user_or_role),
|
||||
transaction_status, std::move(tx_timer), trigger_context_collector, memory_limit, use_monotonic_memory,
|
||||
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr);
|
||||
return PreparedQuery{std::move(header), std::move(parsed_query.required_privileges),
|
||||
[pull_plan = std::move(pull_plan), output_symbols = std::move(output_symbols), summary](
|
||||
@ -2161,7 +2158,8 @@ PreparedQuery PrepareExplainQuery(ParsedQuery parsed_query, std::map<std::string
|
||||
PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::map<std::string, TypedValue> *summary, std::vector<Notification> *notifications,
|
||||
InterpreterContext *interpreter_context, CurrentDB ¤t_db,
|
||||
utils::MemoryResource *execution_memory, std::optional<std::string> const &username,
|
||||
utils::MemoryResource *execution_memory,
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role,
|
||||
std::atomic<TransactionStatus> *transaction_status,
|
||||
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
||||
FrameChangeCollector *frame_change_collector) {
|
||||
@ -2239,37 +2237,37 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
||||
|
||||
rw_type_checker.InferRWType(const_cast<plan::LogicalOperator &>(cypher_query_plan->plan()));
|
||||
|
||||
return PreparedQuery{{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[plan = std::move(cypher_query_plan), parameters = std::move(parsed_inner_query.parameters),
|
||||
summary, dba, interpreter_context, execution_memory, memory_limit, username,
|
||||
// We want to execute the query we are profiling lazily, so we delay
|
||||
// the construction of the corresponding context.
|
||||
stats_and_total_time = std::optional<plan::ProfilingStatsWithTotalTime>{},
|
||||
pull_plan = std::shared_ptr<PullPlanVector>(nullptr), transaction_status, use_monotonic_memory,
|
||||
frame_change_collector, tx_timer = std::move(tx_timer)](
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
// No output symbols are given so that nothing is streamed.
|
||||
if (!stats_and_total_time) {
|
||||
stats_and_total_time =
|
||||
PullPlan(plan, parameters, true, dba, interpreter_context, execution_memory, username,
|
||||
transaction_status, std::move(tx_timer), nullptr, memory_limit,
|
||||
use_monotonic_memory,
|
||||
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr)
|
||||
.Pull(stream, {}, {}, summary);
|
||||
pull_plan = std::make_shared<PullPlanVector>(ProfilingStatsToTable(*stats_and_total_time));
|
||||
}
|
||||
return PreparedQuery{
|
||||
{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[plan = std::move(cypher_query_plan), parameters = std::move(parsed_inner_query.parameters), summary, dba,
|
||||
interpreter_context, execution_memory, memory_limit, user_or_role = std::move(user_or_role),
|
||||
// We want to execute the query we are profiling lazily, so we delay
|
||||
// the construction of the corresponding context.
|
||||
stats_and_total_time = std::optional<plan::ProfilingStatsWithTotalTime>{},
|
||||
pull_plan = std::shared_ptr<PullPlanVector>(nullptr), transaction_status, use_monotonic_memory,
|
||||
frame_change_collector, tx_timer = std::move(tx_timer)](
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
// No output symbols are given so that nothing is streamed.
|
||||
if (!stats_and_total_time) {
|
||||
stats_and_total_time =
|
||||
PullPlan(plan, parameters, true, dba, interpreter_context, execution_memory, std::move(user_or_role),
|
||||
transaction_status, std::move(tx_timer), nullptr, memory_limit, use_monotonic_memory,
|
||||
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr)
|
||||
.Pull(stream, {}, {}, summary);
|
||||
pull_plan = std::make_shared<PullPlanVector>(ProfilingStatsToTable(*stats_and_total_time));
|
||||
}
|
||||
|
||||
MG_ASSERT(stats_and_total_time, "Failed to execute the query!");
|
||||
MG_ASSERT(stats_and_total_time, "Failed to execute the query!");
|
||||
|
||||
if (pull_plan->Pull(stream, n)) {
|
||||
summary->insert_or_assign("profile", ProfilingStatsToJson(*stats_and_total_time).dump());
|
||||
return QueryHandlerResult::ABORT;
|
||||
}
|
||||
if (pull_plan->Pull(stream, n)) {
|
||||
summary->insert_or_assign("profile", ProfilingStatsToJson(*stats_and_total_time).dump());
|
||||
return QueryHandlerResult::ABORT;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
},
|
||||
rw_type_checker.type};
|
||||
return std::nullopt;
|
||||
},
|
||||
rw_type_checker.type};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareDumpQuery(ParsedQuery parsed_query, CurrentDB ¤t_db) {
|
||||
@ -2693,26 +2691,22 @@ PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transa
|
||||
|
||||
auto callback = HandleAuthQuery(auth_query, interpreter_context, parsed_query.parameters, interpreter);
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>(nullptr),
|
||||
interpreter_context]( // NOLINT
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
if (!pull_plan) {
|
||||
// Run the specific query
|
||||
auto results = handler();
|
||||
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
|
||||
#ifdef MG_ENTERPRISE
|
||||
// Invalidate auth cache after every type of AuthQuery
|
||||
interpreter_context->auth_checker->ClearCache();
|
||||
#endif
|
||||
}
|
||||
return PreparedQuery{
|
||||
std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>(nullptr)]( // NOLINT
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
if (!pull_plan) {
|
||||
// Run the specific query
|
||||
auto results = handler();
|
||||
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
|
||||
}
|
||||
|
||||
if (pull_plan->Pull(stream, n)) {
|
||||
return QueryHandlerResult::COMMIT;
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
RWType::NONE};
|
||||
if (pull_plan->Pull(stream, n)) {
|
||||
return QueryHandlerResult::COMMIT;
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
RWType::NONE};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
@ -2916,17 +2910,18 @@ TriggerEventType ToTriggerEventType(const TriggerQuery::EventType event_type) {
|
||||
Callback CreateTrigger(TriggerQuery *trigger_query,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
TriggerStore *trigger_store, InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||
std::optional<std::string> owner) {
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||
// Make a copy of the user and pass it to the subsystem
|
||||
auto owner = interpreter_context->auth_checker->GenQueryUser(user_or_role->username(), user_or_role->rolename());
|
||||
return {{},
|
||||
[trigger_name = std::move(trigger_query->trigger_name_),
|
||||
trigger_statement = std::move(trigger_query->statement_), event_type = trigger_query->event_type_,
|
||||
before_commit = trigger_query->before_commit_, trigger_store, interpreter_context, dba, user_parameters,
|
||||
owner = std::move(owner)]() mutable -> std::vector<std::vector<TypedValue>> {
|
||||
trigger_store->AddTrigger(std::move(trigger_name), trigger_statement, user_parameters,
|
||||
ToTriggerEventType(event_type),
|
||||
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT,
|
||||
&interpreter_context->ast_cache, dba, interpreter_context->config.query,
|
||||
std::move(owner), interpreter_context->auth_checker);
|
||||
trigger_store->AddTrigger(
|
||||
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
||||
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT,
|
||||
&interpreter_context->ast_cache, dba, interpreter_context->config.query, std::move(owner));
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::TriggersCreated);
|
||||
return {};
|
||||
}};
|
||||
@ -2968,7 +2963,7 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
||||
std::vector<Notification> *notifications, CurrentDB ¤t_db,
|
||||
InterpreterContext *interpreter_context,
|
||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||
std::optional<std::string> const &username) {
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||
if (in_explicit_transaction) {
|
||||
throw TriggerModificationInMulticommandTxException();
|
||||
}
|
||||
@ -2982,8 +2977,9 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
||||
MG_ASSERT(trigger_query);
|
||||
|
||||
std::optional<Notification> trigger_notification;
|
||||
|
||||
auto callback = std::invoke([trigger_query, trigger_store, interpreter_context, dba, &user_parameters,
|
||||
owner = username, &trigger_notification]() mutable {
|
||||
owner = std::move(user_or_role), &trigger_notification]() mutable {
|
||||
switch (trigger_query->action_) {
|
||||
case TriggerQuery::Action::CREATE_TRIGGER:
|
||||
trigger_notification.emplace(SeverityLevel::INFO, NotificationCode::CREATE_TRIGGER,
|
||||
@ -3021,7 +3017,8 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
||||
|
||||
PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, CurrentDB ¤t_db,
|
||||
InterpreterContext *interpreter_context, const std::optional<std::string> &username) {
|
||||
InterpreterContext *interpreter_context,
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||
if (in_explicit_transaction) {
|
||||
throw StreamQueryInMulticommandTxException();
|
||||
}
|
||||
@ -3031,8 +3028,8 @@ PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, bool in_explicit_tran
|
||||
|
||||
auto *stream_query = utils::Downcast<StreamQuery>(parsed_query.query);
|
||||
MG_ASSERT(stream_query);
|
||||
auto callback =
|
||||
HandleStreamQuery(stream_query, parsed_query.parameters, db_acc, interpreter_context, username, notifications);
|
||||
auto callback = HandleStreamQuery(stream_query, parsed_query.parameters, db_acc, interpreter_context,
|
||||
std::move(user_or_role), notifications);
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
@ -3336,7 +3333,7 @@ PreparedQuery PrepareSettingQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, const std::optional<std::string> &username,
|
||||
auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, QueryUserOrRole *user_or_role,
|
||||
Func &&privilege_checker) -> std::vector<std::vector<TypedValue>> {
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
results.reserve(interpreters.size());
|
||||
@ -3356,11 +3353,21 @@ auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, con
|
||||
static std::string all;
|
||||
return interpreter->current_db_.db_acc_ ? interpreter->current_db_.db_acc_->get()->name() : all;
|
||||
};
|
||||
if (transaction_id.has_value() &&
|
||||
(interpreter->username_ == username || privilege_checker(get_interpreter_db_name()))) {
|
||||
|
||||
auto same_user = [](const auto &lv, const auto &rv) {
|
||||
if (lv.get() == rv) return true;
|
||||
if (lv && rv) return *lv == *rv;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (transaction_id.has_value() && (same_user(interpreter->user_or_role_, user_or_role) ||
|
||||
privilege_checker(user_or_role, get_interpreter_db_name()))) {
|
||||
const auto &typed_queries = interpreter->GetQueries();
|
||||
results.push_back({TypedValue(interpreter->username_.value_or("")),
|
||||
TypedValue(std::to_string(transaction_id.value())), TypedValue(typed_queries)});
|
||||
results.push_back(
|
||||
{TypedValue(interpreter->user_or_role_
|
||||
? (interpreter->user_or_role_->username() ? *interpreter->user_or_role_->username() : "")
|
||||
: ""),
|
||||
TypedValue(std::to_string(transaction_id.value())), TypedValue(typed_queries)});
|
||||
// Handle user-defined metadata
|
||||
std::map<std::string, TypedValue> metadata_tv;
|
||||
if (interpreter->metadata_) {
|
||||
@ -3375,17 +3382,19 @@ auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, con
|
||||
}
|
||||
|
||||
Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
||||
const std::optional<std::string> &username, const Parameters ¶meters,
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role, const Parameters ¶meters,
|
||||
InterpreterContext *interpreter_context) {
|
||||
auto privilege_checker = [username, auth_checker = interpreter_context->auth_checker](std::string const &db_name) {
|
||||
return auth_checker->IsUserAuthorized(username, {query::AuthQuery::Privilege::TRANSACTION_MANAGEMENT}, db_name);
|
||||
auto privilege_checker = [](QueryUserOrRole *user_or_role, std::string const &db_name) {
|
||||
return user_or_role && user_or_role->IsAuthorized({query::AuthQuery::Privilege::TRANSACTION_MANAGEMENT}, db_name,
|
||||
&query::up_to_date_policy);
|
||||
};
|
||||
|
||||
Callback callback;
|
||||
switch (transaction_query->action_) {
|
||||
case TransactionQueueQuery::Action::SHOW_TRANSACTIONS: {
|
||||
auto show_transactions = [username, privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
||||
return ShowTransactions(interpreters, username, privilege_checker);
|
||||
auto show_transactions = [user_or_role = std::move(user_or_role),
|
||||
privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
||||
return ShowTransactions(interpreters, user_or_role.get(), privilege_checker);
|
||||
};
|
||||
callback.header = {"username", "transaction_id", "query", "metadata"};
|
||||
callback.fn = [interpreter_context, show_transactions = std::move(show_transactions)] {
|
||||
@ -3403,9 +3412,10 @@ Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
||||
return std::string(expression->Accept(evaluator).ValueString());
|
||||
});
|
||||
callback.header = {"transaction_id", "killed"};
|
||||
callback.fn = [interpreter_context, maybe_kill_transaction_ids = std::move(maybe_kill_transaction_ids), username,
|
||||
callback.fn = [interpreter_context, maybe_kill_transaction_ids = std::move(maybe_kill_transaction_ids),
|
||||
user_or_role = std::move(user_or_role),
|
||||
privilege_checker = std::move(privilege_checker)]() mutable {
|
||||
return interpreter_context->TerminateTransactions(std::move(maybe_kill_transaction_ids), username,
|
||||
return interpreter_context->TerminateTransactions(std::move(maybe_kill_transaction_ids), user_or_role.get(),
|
||||
std::move(privilege_checker));
|
||||
};
|
||||
break;
|
||||
@ -3415,12 +3425,12 @@ Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
||||
return callback;
|
||||
}
|
||||
|
||||
PreparedQuery PrepareTransactionQueueQuery(ParsedQuery parsed_query, const std::optional<std::string> &username,
|
||||
PreparedQuery PrepareTransactionQueueQuery(ParsedQuery parsed_query, std::shared_ptr<QueryUserOrRole> user_or_role,
|
||||
InterpreterContext *interpreter_context) {
|
||||
auto *transaction_queue_query = utils::Downcast<TransactionQueueQuery>(parsed_query.query);
|
||||
MG_ASSERT(transaction_queue_query);
|
||||
auto callback =
|
||||
HandleTransactionQueueQuery(transaction_queue_query, username, parsed_query.parameters, interpreter_context);
|
||||
auto callback = HandleTransactionQueueQuery(transaction_queue_query, std::move(user_or_role), parsed_query.parameters,
|
||||
interpreter_context);
|
||||
|
||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
@ -4053,7 +4063,7 @@ PreparedQuery PrepareMultiDatabaseQuery(ParsedQuery parsed_query, CurrentDB &cur
|
||||
}
|
||||
|
||||
PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterContext *interpreter_context,
|
||||
const std::optional<std::string> &username) {
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
@ -4064,7 +4074,8 @@ PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterCon
|
||||
|
||||
Callback callback;
|
||||
callback.header = {"Name"};
|
||||
callback.fn = [auth, db_handler, username]() mutable -> std::vector<std::vector<TypedValue>> {
|
||||
callback.fn = [auth, db_handler,
|
||||
user_or_role = std::move(user_or_role)]() mutable -> std::vector<std::vector<TypedValue>> {
|
||||
std::vector<std::vector<TypedValue>> status;
|
||||
auto gen_status = [&]<typename T, typename K>(T all, K denied) {
|
||||
Sort(all);
|
||||
@ -4086,12 +4097,12 @@ PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterCon
|
||||
status.erase(iter, status.end());
|
||||
};
|
||||
|
||||
if (!username) {
|
||||
if (!user_or_role || !*user_or_role) {
|
||||
// No user, return all
|
||||
gen_status(db_handler->All(), std::vector<TypedValue>{});
|
||||
} else {
|
||||
// User has a subset of accessible dbs; this is synched with the SessionContextHandler
|
||||
const auto &db_priv = auth->GetDatabasePrivileges(*username);
|
||||
const auto &db_priv = auth->GetDatabasePrivileges(user_or_role->key());
|
||||
const auto &allowed = db_priv[0][0];
|
||||
const auto &denied = db_priv[0][1].ValueList();
|
||||
if (allowed.IsString() && allowed.ValueString() == auth::kAllDatabases) {
|
||||
@ -4159,6 +4170,7 @@ void Interpreter::SetCurrentDB(std::string_view db_name, bool in_explicit_db) {
|
||||
Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
QueryExtras const &extras) {
|
||||
MG_ASSERT(user_or_role_, "Trying to prepare a query without a query user.");
|
||||
// Handle transaction control queries.
|
||||
const auto upper_case_query = utils::ToUpperCase(query_string);
|
||||
const auto trimmed_query = utils::Trim(upper_case_query);
|
||||
@ -4301,7 +4313,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
frame_change_collector_.emplace();
|
||||
if (utils::Downcast<CypherQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareCypherQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
||||
current_db_, memory_resource, &query_execution->notifications, username_,
|
||||
current_db_, memory_resource, &query_execution->notifications, user_or_role_,
|
||||
&transaction_status_, current_timeout_timer_, &*frame_change_collector_);
|
||||
} else if (utils::Downcast<ExplainQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareExplainQuery(std::move(parsed_query), &query_execution->summary,
|
||||
@ -4309,7 +4321,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
} else if (utils::Downcast<ProfileQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareProfileQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
||||
&query_execution->notifications, interpreter_context_, current_db_,
|
||||
&query_execution->execution_memory_with_exception, username_,
|
||||
&query_execution->execution_memory_with_exception, user_or_role_,
|
||||
&transaction_status_, current_timeout_timer_, &*frame_change_collector_);
|
||||
} else if (utils::Downcast<DumpQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareDumpQuery(std::move(parsed_query), current_db_);
|
||||
@ -4353,11 +4365,11 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
} else if (utils::Downcast<TriggerQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareTriggerQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
current_db_, interpreter_context_, params, username_);
|
||||
current_db_, interpreter_context_, params, user_or_role_);
|
||||
} else if (utils::Downcast<StreamQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareStreamQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
current_db_, interpreter_context_, username_);
|
||||
current_db_, interpreter_context_, user_or_role_);
|
||||
} else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, current_db_, this);
|
||||
} else if (utils::Downcast<CreateSnapshotQuery>(parsed_query.query)) {
|
||||
@ -4378,7 +4390,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
if (in_explicit_transaction_) {
|
||||
throw TransactionQueueInMulticommandTxException();
|
||||
}
|
||||
prepared_query = PrepareTransactionQueueQuery(std::move(parsed_query), username_, interpreter_context_);
|
||||
prepared_query = PrepareTransactionQueueQuery(std::move(parsed_query), user_or_role_, interpreter_context_);
|
||||
} else if (utils::Downcast<MultiDatabaseQuery>(parsed_query.query)) {
|
||||
if (in_explicit_transaction_) {
|
||||
throw MultiDatabaseQueryInMulticommandTxException();
|
||||
@ -4388,7 +4400,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
prepared_query =
|
||||
PrepareMultiDatabaseQuery(std::move(parsed_query), current_db_, interpreter_context_, on_change_, *this);
|
||||
} else if (utils::Downcast<ShowDatabasesQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareShowDatabasesQuery(std::move(parsed_query), interpreter_context_, username_);
|
||||
prepared_query = PrepareShowDatabasesQuery(std::move(parsed_query), interpreter_context_, user_or_role_);
|
||||
} else if (utils::Downcast<EdgeImportModeQuery>(parsed_query.query)) {
|
||||
if (in_explicit_transaction_) {
|
||||
throw EdgeImportModeModificationInMulticommandTxException();
|
||||
@ -4464,6 +4476,12 @@ std::vector<TypedValue> Interpreter::GetQueries() {
|
||||
|
||||
void Interpreter::Abort() {
|
||||
bool decrement = true;
|
||||
|
||||
// System tx
|
||||
// TODO Implement system transaction scope and the ability to abort
|
||||
system_transaction_.reset();
|
||||
|
||||
// Data tx
|
||||
auto expected = TransactionStatus::ACTIVE;
|
||||
while (!transaction_status_.compare_exchange_weak(expected, TransactionStatus::STARTED_ROLLBACK)) {
|
||||
if (expected == TransactionStatus::TERMINATED || expected == TransactionStatus::IDLE) {
|
||||
@ -4515,8 +4533,7 @@ void RunTriggersAfterCommit(dbms::DatabaseAccess db_acc, InterpreterContext *int
|
||||
trigger_context.AdaptForAccessor(&db_accessor);
|
||||
try {
|
||||
trigger.Execute(&db_accessor, &execution_memory, flags::run_time::GetExecutionTimeout(),
|
||||
&interpreter_context->is_shutting_down, transaction_status, trigger_context,
|
||||
interpreter_context->auth_checker);
|
||||
&interpreter_context->is_shutting_down, transaction_status, trigger_context);
|
||||
} catch (const utils::BasicException &exception) {
|
||||
spdlog::warn("Trigger '{}' failed with exception:\n{}", trigger.Name(), exception.what());
|
||||
db_accessor.Abort();
|
||||
@ -4673,8 +4690,7 @@ void Interpreter::Commit() {
|
||||
AdvanceCommand();
|
||||
try {
|
||||
trigger.Execute(&*current_db_.execution_db_accessor_, &execution_memory, flags::run_time::GetExecutionTimeout(),
|
||||
&interpreter_context_->is_shutting_down, &transaction_status_, *trigger_context,
|
||||
interpreter_context_->auth_checker);
|
||||
&interpreter_context_->is_shutting_down, &transaction_status_, *trigger_context);
|
||||
} catch (const utils::BasicException &e) {
|
||||
throw utils::BasicException(
|
||||
fmt::format("Trigger '{}' caused the transaction to fail.\nException: {}", trigger.Name(), e.what()));
|
||||
@ -4789,7 +4805,7 @@ void Interpreter::SetNextTransactionIsolationLevel(const storage::IsolationLevel
|
||||
void Interpreter::SetSessionIsolationLevel(const storage::IsolationLevel isolation_level) {
|
||||
interpreter_isolation_level.emplace(isolation_level);
|
||||
}
|
||||
void Interpreter::ResetUser() { username_.reset(); }
|
||||
void Interpreter::SetUser(std::string_view username) { username_ = username; }
|
||||
void Interpreter::ResetUser() { user_or_role_.reset(); }
|
||||
void Interpreter::SetUser(std::shared_ptr<QueryUserOrRole> user_or_role) { user_or_role_ = std::move(user_or_role); }
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
@ -210,7 +210,7 @@ class Interpreter final {
|
||||
std::optional<std::string> db;
|
||||
};
|
||||
|
||||
std::optional<std::string> username_;
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role_{};
|
||||
bool in_explicit_transaction_{false};
|
||||
CurrentDB current_db_;
|
||||
|
||||
@ -300,7 +300,7 @@ class Interpreter final {
|
||||
|
||||
void ResetUser();
|
||||
|
||||
void SetUser(std::string_view username);
|
||||
void SetUser(std::shared_ptr<QueryUserOrRole> user);
|
||||
|
||||
std::optional<memgraph::system::Transaction> system_transaction_{};
|
||||
|
||||
|
@ -35,13 +35,13 @@ InterpreterContext::InterpreterContext(InterpreterConfig interpreter_config, dbm
|
||||
}
|
||||
|
||||
std::vector<std::vector<TypedValue>> InterpreterContext::TerminateTransactions(
|
||||
std::vector<std::string> maybe_kill_transaction_ids, const std::optional<std::string> &username,
|
||||
std::function<bool(std::string const &)> privilege_checker) {
|
||||
std::vector<std::string> maybe_kill_transaction_ids, QueryUserOrRole *user_or_role,
|
||||
std::function<bool(QueryUserOrRole *, std::string const &)> privilege_checker) {
|
||||
auto not_found_midpoint = maybe_kill_transaction_ids.end();
|
||||
|
||||
// Multiple simultaneous TERMINATE TRANSACTIONS aren't allowed
|
||||
// TERMINATE and SHOW TRANSACTIONS are mutually exclusive
|
||||
interpreters.WithLock([¬_found_midpoint, &maybe_kill_transaction_ids, username,
|
||||
interpreters.WithLock([¬_found_midpoint, &maybe_kill_transaction_ids, user_or_role,
|
||||
privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
||||
for (Interpreter *interpreter : interpreters) {
|
||||
TransactionStatus alive_status = TransactionStatus::ACTIVE;
|
||||
@ -73,7 +73,15 @@ std::vector<std::vector<TypedValue>> InterpreterContext::TerminateTransactions(
|
||||
static std::string all;
|
||||
return interpreter->current_db_.db_acc_ ? interpreter->current_db_.db_acc_->get()->name() : all;
|
||||
};
|
||||
if (interpreter->username_ == username || privilege_checker(get_interpreter_db_name())) {
|
||||
|
||||
auto same_user = [](const auto &lv, const auto &rv) {
|
||||
if (lv.get() == rv) return true;
|
||||
if (lv && rv) return *lv == *rv;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (same_user(interpreter->user_or_role_, user_or_role) ||
|
||||
privilege_checker(user_or_role, get_interpreter_db_name())) {
|
||||
killed = true; // Note: this is used by the above `clean_status` (OnScopeExit)
|
||||
spdlog::warn("Transaction {} successfully killed", transaction_id);
|
||||
} else {
|
||||
|
@ -46,6 +46,7 @@ constexpr uint64_t kInterpreterTransactionInitialId = 1ULL << 63U;
|
||||
class AuthQueryHandler;
|
||||
class AuthChecker;
|
||||
class Interpreter;
|
||||
struct QueryUserOrRole;
|
||||
|
||||
/**
|
||||
* Holds data shared between multiple `Interpreter` instances (which might be
|
||||
@ -95,8 +96,8 @@ struct InterpreterContext {
|
||||
void Shutdown() { is_shutting_down.store(true, std::memory_order_release); }
|
||||
|
||||
std::vector<std::vector<TypedValue>> TerminateTransactions(
|
||||
std::vector<std::string> maybe_kill_transaction_ids, const std::optional<std::string> &username,
|
||||
std::function<bool(std::string const &)> privilege_checker);
|
||||
std::vector<std::string> maybe_kill_transaction_ids, QueryUserOrRole *user_or_role,
|
||||
std::function<bool(QueryUserOrRole *, std::string const &)> privilege_checker);
|
||||
};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
@ -1549,15 +1549,15 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
|
||||
|
||||
// for the given (edge, vertex) pair checks if they satisfy the
|
||||
// "where" condition. if so, places them in the to_visit_ structure.
|
||||
auto expand_pair = [this, &evaluator, &frame, &context](EdgeAccessor edge, VertexAccessor vertex) {
|
||||
auto expand_pair = [this, &evaluator, &frame, &context](EdgeAccessor edge, VertexAccessor vertex) -> bool {
|
||||
// if we already processed the given vertex it doesn't get expanded
|
||||
if (processed_.find(vertex) != processed_.end()) return;
|
||||
if (processed_.find(vertex) != processed_.end()) return false;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
frame[self_.filter_lambda_.inner_edge_symbol] = edge;
|
||||
@ -1576,9 +1576,9 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
|
||||
TypedValue result = self_.filter_lambda_.expression->Accept(evaluator);
|
||||
switch (result.type()) {
|
||||
case TypedValue::Type::Null:
|
||||
return;
|
||||
return true;
|
||||
case TypedValue::Type::Bool:
|
||||
if (!result.ValueBool()) return;
|
||||
if (!result.ValueBool()) return true;
|
||||
break;
|
||||
default:
|
||||
throw QueryRuntimeException("Expansion condition must evaluate to boolean or null.");
|
||||
@ -1586,10 +1586,11 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
|
||||
}
|
||||
to_visit_next_.emplace_back(edge, vertex, std::move(curr_acc_path));
|
||||
processed_.emplace(vertex, edge);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto restore_frame_state_after_expansion = [this, &frame]() {
|
||||
if (self_.filter_lambda_.accumulated_path_symbol) {
|
||||
auto restore_frame_state_after_expansion = [this, &frame](bool was_expanded) {
|
||||
if (was_expanded && self_.filter_lambda_.accumulated_path_symbol) {
|
||||
frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath().Shrink();
|
||||
}
|
||||
};
|
||||
@ -1601,15 +1602,15 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::IN) {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
|
||||
for (const auto &edge : out_edges) {
|
||||
expand_pair(edge, edge.To());
|
||||
restore_frame_state_after_expansion();
|
||||
bool was_expanded = expand_pair(edge, edge.To());
|
||||
restore_frame_state_after_expansion(was_expanded);
|
||||
}
|
||||
}
|
||||
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
|
||||
for (const auto &edge : in_edges) {
|
||||
expand_pair(edge, edge.From());
|
||||
restore_frame_state_after_expansion();
|
||||
bool was_expanded = expand_pair(edge, edge.From());
|
||||
restore_frame_state_after_expansion(was_expanded);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1800,18 +1801,8 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
|
||||
// For the given (edge, vertex, weight, depth) tuple checks if they
|
||||
// satisfy the "where" condition. if so, places them in the priority
|
||||
// queue.
|
||||
auto expand_pair = [this, &evaluator, &frame, &create_state, &context](
|
||||
const EdgeAccessor &edge, const VertexAccessor &vertex, const TypedValue &total_weight,
|
||||
int64_t depth) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(vertex, storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto expand_pair = [this, &evaluator, &frame, &create_state](const EdgeAccessor &edge, const VertexAccessor &vertex,
|
||||
const TypedValue &total_weight, int64_t depth) {
|
||||
frame[self_.weight_lambda_->inner_edge_symbol] = edge;
|
||||
frame[self_.weight_lambda_->inner_node_symbol] = vertex;
|
||||
TypedValue next_weight = CalculateNextWeight(self_.weight_lambda_, total_weight, evaluator);
|
||||
@ -1854,11 +1845,19 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
|
||||
// Populates the priority queue structure with expansions
|
||||
// from the given vertex. skips expansions that don't satisfy
|
||||
// the "where" condition.
|
||||
auto expand_from_vertex = [this, &expand_pair, &restore_frame_state_after_expansion](
|
||||
auto expand_from_vertex = [this, &context, &expand_pair, &restore_frame_state_after_expansion](
|
||||
const VertexAccessor &vertex, const TypedValue &weight, int64_t depth) {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::IN) {
|
||||
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
|
||||
for (const auto &edge : out_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge.To(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
expand_pair(edge, edge.To(), weight, depth);
|
||||
restore_frame_state_after_expansion();
|
||||
}
|
||||
@ -1866,6 +1865,14 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
|
||||
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
|
||||
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
|
||||
for (const auto &edge : in_edges) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
|
||||
!(context.auth_checker->Has(edge.From(), storage::View::OLD,
|
||||
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
|
||||
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
expand_pair(edge, edge.From(), weight, depth);
|
||||
restore_frame_state_after_expansion();
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
|
||||
auto *property_lookup = storage.Create<PropertyLookup>(atom->filter_lambda_.inner_edge, prop_pair.first);
|
||||
auto *prop_equal = storage.Create<EqualOperator>(property_lookup, prop_pair.second);
|
||||
// Currently, variable expand has no gains if we set PropertyFilter.
|
||||
all_filters_.emplace_back(FilterInfo{FilterInfo::Type::Generic, prop_equal, collector.symbols_});
|
||||
all_filters_.emplace_back(FilterInfo::Type::Generic, prop_equal, collector.symbols_);
|
||||
}
|
||||
{
|
||||
collector.symbols_.clear();
|
||||
@ -328,9 +328,9 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
|
||||
auto *prop_equal = storage.Create<EqualOperator>(property_lookup, prop_pair.second);
|
||||
// Currently, variable expand has no gains if we set PropertyFilter.
|
||||
all_filters_.emplace_back(
|
||||
FilterInfo{FilterInfo::Type::Generic,
|
||||
storage.Create<All>(identifier, atom->identifier_, storage.Create<Where>(prop_equal)),
|
||||
collector.symbols_});
|
||||
FilterInfo::Type::Generic,
|
||||
storage.Create<All>(identifier, atom->identifier_, storage.Create<Where>(prop_equal)),
|
||||
collector.symbols_);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -639,6 +639,12 @@ void AddMatching(const Match &match, SymbolTable &symbol_table, AstStorage &stor
|
||||
}
|
||||
}
|
||||
|
||||
PatternFilterVisitor::PatternFilterVisitor(SymbolTable &symbol_table, AstStorage &storage)
|
||||
: symbol_table_(symbol_table), storage_(storage) {}
|
||||
PatternFilterVisitor::PatternFilterVisitor(const PatternFilterVisitor &) = default;
|
||||
PatternFilterVisitor::PatternFilterVisitor(PatternFilterVisitor &&) noexcept = default;
|
||||
PatternFilterVisitor::~PatternFilterVisitor() = default;
|
||||
|
||||
void PatternFilterVisitor::Visit(Exists &op) {
|
||||
std::vector<Pattern *> patterns;
|
||||
patterns.push_back(op.pattern_);
|
||||
@ -652,6 +658,8 @@ void PatternFilterVisitor::Visit(Exists &op) {
|
||||
matchings_.push_back(std::move(filter_matching));
|
||||
}
|
||||
|
||||
std::vector<FilterMatching> PatternFilterVisitor::getMatchings() { return matchings_; }
|
||||
|
||||
static void ParseForeach(query::Foreach &foreach, SingleQueryPart &query_part, AstStorage &storage,
|
||||
SymbolTable &symbol_table) {
|
||||
for (auto *clause : foreach.clauses_) {
|
||||
@ -723,4 +731,18 @@ QueryParts CollectQueryParts(SymbolTable &symbol_table, AstStorage &storage, Cyp
|
||||
return QueryParts{query_parts, distinct};
|
||||
}
|
||||
|
||||
FilterInfo::FilterInfo(Type type, Expression *expression, std::unordered_set<Symbol> used_symbols,
|
||||
std::optional<PropertyFilter> property_filter, std::optional<IdFilter> id_filter)
|
||||
: type(type),
|
||||
expression(expression),
|
||||
used_symbols(std::move(used_symbols)),
|
||||
property_filter(std::move(property_filter)),
|
||||
id_filter(std::move(id_filter)),
|
||||
matchings({}) {}
|
||||
FilterInfo::FilterInfo(const FilterInfo &) = default;
|
||||
FilterInfo &FilterInfo::operator=(const FilterInfo &) = default;
|
||||
FilterInfo::FilterInfo(FilterInfo &&) noexcept = default;
|
||||
FilterInfo &FilterInfo::operator=(FilterInfo &&) noexcept = default;
|
||||
FilterInfo::~FilterInfo() = default;
|
||||
|
||||
} // namespace memgraph::query::plan
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/frontend/ast/ast_visitor.hpp"
|
||||
#include "query/frontend/semantic/symbol_table.hpp"
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
@ -159,8 +160,12 @@ enum class PatternFilterType { EXISTS };
|
||||
/// Collects matchings from filters that include patterns
|
||||
class PatternFilterVisitor : public ExpressionVisitor<void> {
|
||||
public:
|
||||
explicit PatternFilterVisitor(SymbolTable &symbol_table, AstStorage &storage)
|
||||
: symbol_table_(symbol_table), storage_(storage) {}
|
||||
explicit PatternFilterVisitor(SymbolTable &symbol_table, AstStorage &storage);
|
||||
PatternFilterVisitor(const PatternFilterVisitor &);
|
||||
PatternFilterVisitor &operator=(const PatternFilterVisitor &) = delete;
|
||||
PatternFilterVisitor(PatternFilterVisitor &&) noexcept;
|
||||
PatternFilterVisitor &operator=(PatternFilterVisitor &&) noexcept = delete;
|
||||
~PatternFilterVisitor() override;
|
||||
|
||||
using ExpressionVisitor<void>::Visit;
|
||||
|
||||
@ -232,7 +237,7 @@ class PatternFilterVisitor : public ExpressionVisitor<void> {
|
||||
void Visit(RegexMatch &op) override{};
|
||||
void Visit(PatternComprehension &op) override{};
|
||||
|
||||
std::vector<FilterMatching> getMatchings() { return matchings_; }
|
||||
std::vector<FilterMatching> getMatchings();
|
||||
|
||||
SymbolTable &symbol_table_;
|
||||
AstStorage &storage_;
|
||||
@ -298,9 +303,23 @@ struct FilterInfo {
|
||||
/// elements.
|
||||
enum class Type { Generic, Label, Property, Id, Pattern };
|
||||
|
||||
Type type;
|
||||
// FilterInfo is tricky because FilterMatching is not yet defined:
|
||||
// * if no declared constructor -> FilterInfo is std::__is_complete_or_unbounded
|
||||
// * if any user-declared constructor -> non-aggregate type -> no designated initializers are possible
|
||||
// * IMPORTANT: Matchings will always be initialized to an empty container.
|
||||
explicit FilterInfo(Type type = Type::Generic, Expression *expression = nullptr,
|
||||
std::unordered_set<Symbol> used_symbols = {}, std::optional<PropertyFilter> property_filter = {},
|
||||
std::optional<IdFilter> id_filter = {});
|
||||
// All other constructors are also defined in the cpp file because this struct is incomplete here.
|
||||
FilterInfo(const FilterInfo &);
|
||||
FilterInfo &operator=(const FilterInfo &);
|
||||
FilterInfo(FilterInfo &&) noexcept;
|
||||
FilterInfo &operator=(FilterInfo &&) noexcept;
|
||||
~FilterInfo();
|
||||
|
||||
Type type{Type::Generic};
|
||||
/// The original filter expression which must be satisfied.
|
||||
Expression *expression;
|
||||
Expression *expression{nullptr};
|
||||
/// Set of used symbols by the filter @c expression.
|
||||
std::unordered_set<Symbol> used_symbols{};
|
||||
/// Labels for Type::Label filtering.
|
||||
@ -310,7 +329,8 @@ struct FilterInfo {
|
||||
/// Information for Type::Id filtering.
|
||||
std::optional<IdFilter> id_filter{};
|
||||
/// Matchings for filters that include patterns
|
||||
std::vector<FilterMatching> matchings{};
|
||||
/// NOTE: The vector is not defined here because FilterMatching is forward declared above.
|
||||
std::vector<FilterMatching> matchings;
|
||||
};
|
||||
|
||||
/// Stores information on filters used inside the @c Matching of a @c QueryPart.
|
||||
@ -329,34 +349,15 @@ class Filters final {
|
||||
|
||||
auto empty() const { return all_filters_.empty(); }
|
||||
|
||||
auto erase(iterator pos) { return all_filters_.erase(pos); }
|
||||
auto erase(const_iterator pos) { return all_filters_.erase(pos); }
|
||||
auto erase(iterator first, iterator last) { return all_filters_.erase(first, last); }
|
||||
auto erase(const_iterator first, const_iterator last) { return all_filters_.erase(first, last); }
|
||||
auto erase(iterator pos) -> iterator;
|
||||
auto erase(const_iterator pos) -> iterator;
|
||||
auto erase(iterator first, iterator last) -> iterator;
|
||||
auto erase(const_iterator first, const_iterator last) -> iterator;
|
||||
|
||||
void SetFilters(std::vector<FilterInfo> &&all_filters) { all_filters_ = std::move(all_filters); }
|
||||
|
||||
auto FilteredLabels(const Symbol &symbol) const {
|
||||
std::unordered_set<LabelIx> labels;
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Label && utils::Contains(filter.used_symbols, symbol)) {
|
||||
MG_ASSERT(filter.used_symbols.size() == 1U, "Expected a single used symbol for label filter");
|
||||
labels.insert(filter.labels.begin(), filter.labels.end());
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
auto FilteredProperties(const Symbol &symbol) const -> std::unordered_set<PropertyIx> {
|
||||
std::unordered_set<PropertyIx> properties;
|
||||
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Property && filter.property_filter->symbol_ == symbol) {
|
||||
properties.insert(filter.property_filter->property_);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
auto FilteredLabels(const Symbol &symbol) const -> std::unordered_set<LabelIx>;
|
||||
auto FilteredProperties(const Symbol &symbol) const -> std::unordered_set<PropertyIx>;
|
||||
|
||||
/// Remove a filter; may invalidate iterators.
|
||||
/// Removal is done by comparing only the expression, so that multiple
|
||||
@ -370,26 +371,10 @@ class Filters final {
|
||||
std::vector<Expression *> *removed_filters = nullptr);
|
||||
|
||||
/// Returns a vector of FilterInfo for properties.
|
||||
auto PropertyFilters(const Symbol &symbol) const {
|
||||
std::vector<FilterInfo> filters;
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Property && filter.property_filter->symbol_ == symbol) {
|
||||
filters.push_back(filter);
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
auto PropertyFilters(const Symbol &symbol) const -> std::vector<FilterInfo>;
|
||||
|
||||
/// Return a vector of FilterInfo for ID equality filtering.
|
||||
auto IdFilters(const Symbol &symbol) const {
|
||||
std::vector<FilterInfo> filters;
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Id && filter.id_filter->symbol_ == symbol) {
|
||||
filters.push_back(filter);
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
auto IdFilters(const Symbol &symbol) const -> std::vector<FilterInfo>;
|
||||
|
||||
/// Collects filtering information from a pattern.
|
||||
///
|
||||
@ -459,6 +444,57 @@ struct FilterMatching : Matching {
|
||||
std::optional<Symbol> symbol;
|
||||
};
|
||||
|
||||
inline auto Filters::erase(Filters::iterator pos) -> iterator { return all_filters_.erase(pos); }
|
||||
inline auto Filters::erase(Filters::const_iterator pos) -> iterator { return all_filters_.erase(pos); }
|
||||
inline auto Filters::erase(Filters::iterator first, Filters::iterator last) -> iterator {
|
||||
return all_filters_.erase(first, last);
|
||||
}
|
||||
inline auto Filters::erase(Filters::const_iterator first, Filters::const_iterator last) -> iterator {
|
||||
return all_filters_.erase(first, last);
|
||||
}
|
||||
|
||||
inline auto Filters::FilteredLabels(const Symbol &symbol) const -> std::unordered_set<LabelIx> {
|
||||
std::unordered_set<LabelIx> labels;
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Label && utils::Contains(filter.used_symbols, symbol)) {
|
||||
MG_ASSERT(filter.used_symbols.size() == 1U, "Expected a single used symbol for label filter");
|
||||
labels.insert(filter.labels.begin(), filter.labels.end());
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
inline auto Filters::FilteredProperties(const Symbol &symbol) const -> std::unordered_set<PropertyIx> {
|
||||
std::unordered_set<PropertyIx> properties;
|
||||
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Property && filter.property_filter->symbol_ == symbol) {
|
||||
properties.insert(filter.property_filter->property_);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
inline auto Filters::PropertyFilters(const Symbol &symbol) const -> std::vector<FilterInfo> {
|
||||
std::vector<FilterInfo> filters;
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Property && filter.property_filter->symbol_ == symbol) {
|
||||
filters.push_back(filter);
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
inline auto Filters::IdFilters(const Symbol &symbol) const -> std::vector<FilterInfo> {
|
||||
std::vector<FilterInfo> filters;
|
||||
for (const auto &filter : all_filters_) {
|
||||
if (filter.type == FilterInfo::Type::Id && filter.id_filter->symbol_ == symbol) {
|
||||
filters.push_back(filter);
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
/// @brief Represents a read (+ write) part of a query. Parts are split on
|
||||
/// `WITH` clauses.
|
||||
///
|
||||
|
82
src/query/procedure/fmt.hpp
Normal file
82
src/query/procedure/fmt.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
// 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 "mg_procedure.h"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
inline std::string ToString(const mgp_log_level &log_level) {
|
||||
switch (log_level) {
|
||||
case mgp_log_level::MGP_LOG_LEVEL_CRITICAL:
|
||||
return "CRITICAL";
|
||||
case mgp_log_level::MGP_LOG_LEVEL_ERROR:
|
||||
return "ERROR";
|
||||
case mgp_log_level::MGP_LOG_LEVEL_WARN:
|
||||
return "WARN";
|
||||
case mgp_log_level::MGP_LOG_LEVEL_INFO:
|
||||
return "INFO";
|
||||
case mgp_log_level::MGP_LOG_LEVEL_DEBUG:
|
||||
return "DEBUG";
|
||||
case mgp_log_level::MGP_LOG_LEVEL_TRACE:
|
||||
return "TRACE";
|
||||
}
|
||||
LOG_FATAL("ToString of a wrong mgp_log_level -> check missing switch case");
|
||||
}
|
||||
inline std::ostream &operator<<(std::ostream &os, const mgp_log_level &log_level) {
|
||||
os << ToString(log_level);
|
||||
return os;
|
||||
}
|
||||
template <>
|
||||
class fmt::formatter<mgp_log_level> : public fmt::ostream_formatter {};
|
||||
|
||||
inline std::string ToString(const mgp_error &error) {
|
||||
switch (error) {
|
||||
case mgp_error::MGP_ERROR_NO_ERROR:
|
||||
return "NO ERROR";
|
||||
case mgp_error::MGP_ERROR_UNKNOWN_ERROR:
|
||||
return "UNKNOWN ERROR";
|
||||
case mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE:
|
||||
return "UNABLE TO ALLOCATE ERROR";
|
||||
case mgp_error::MGP_ERROR_INSUFFICIENT_BUFFER:
|
||||
return "INSUFFICIENT BUFFER ERROR";
|
||||
case mgp_error::MGP_ERROR_OUT_OF_RANGE:
|
||||
return "OUT OF RANGE ERROR";
|
||||
case mgp_error::MGP_ERROR_LOGIC_ERROR:
|
||||
return "LOGIC ERROR";
|
||||
case mgp_error::MGP_ERROR_DELETED_OBJECT:
|
||||
return "DELETED OBJECT ERROR";
|
||||
case mgp_error::MGP_ERROR_INVALID_ARGUMENT:
|
||||
return "INVALID ARGUMENT ERROR";
|
||||
case mgp_error::MGP_ERROR_KEY_ALREADY_EXISTS:
|
||||
return "KEY ALREADY EXISTS ERROR";
|
||||
case mgp_error::MGP_ERROR_IMMUTABLE_OBJECT:
|
||||
return "IMMUTABLE OBJECT ERROR";
|
||||
case mgp_error::MGP_ERROR_VALUE_CONVERSION:
|
||||
return "VALUE CONVERSION ERROR";
|
||||
case mgp_error::MGP_ERROR_SERIALIZATION_ERROR:
|
||||
return "SERIALIZATION ERROR";
|
||||
case mgp_error::MGP_ERROR_AUTHORIZATION_ERROR:
|
||||
return "AUTHORIZATION ERROR";
|
||||
}
|
||||
LOG_FATAL("ToString of a wrong mgp_error -> check missing switch case");
|
||||
}
|
||||
inline std::ostream &operator<<(std::ostream &os, const mgp_error &error) {
|
||||
os << ToString(error);
|
||||
return os;
|
||||
}
|
||||
template <>
|
||||
class fmt::formatter<mgp_error> : public fmt::ostream_formatter {};
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "query/procedure/mg_procedure_helpers.hpp"
|
||||
#include "query/procedure/fmt.hpp"
|
||||
|
||||
namespace memgraph::query::procedure {
|
||||
MgpUniquePtr<mgp_value> GetStringValueOrSetError(const char *string, mgp_memory *memory, mgp_result *result) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@ -18,6 +18,7 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mg_procedure.h"
|
||||
#include "query/procedure/fmt.hpp"
|
||||
|
||||
namespace memgraph::query::procedure {
|
||||
template <typename TResult, typename TFunc, typename... TArgs>
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/procedure/cypher_types.hpp"
|
||||
#include "query/procedure/fmt.hpp"
|
||||
#include "query/procedure/mg_procedure_helpers.hpp"
|
||||
#include "query/stream/common.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user