diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..ac62dd3 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.38.10" +} \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/01-feature-request.yml b/.github/ISSUE_TEMPLATE/01-feature-request.yml new file mode 100644 index 0000000..13b9410 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-feature-request.yml @@ -0,0 +1,56 @@ +name: Feature request +description: Suggest an idea or improvement for Defguard +title: "[Feature]: " +labels: + - feature +type: feature + +body: + - type: markdown + attributes: + value: | + Thank you for suggesting a feature. Your feedback helps improve Defguard. + + - type: textarea + id: problem + attributes: + label: Problem description + description: What problem are you trying to solve? Attach screenshots or recording if it helps illustrate the problem. + placeholder: | + Describe the limitation, friction, or missing capability. + Example: "Users cannot restrict access based on device posture..." + validations: + required: true + + - type: textarea + id: proposed_solution + attributes: + label: Proposed solution + description: Describe the solution you would like. Attach mockups or diagrams if you have them. + placeholder: | + Describe the desired behavior, API, UI, or workflow. + Include examples if possible. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Any alternative solutions or workarounds you've considered + placeholder: | + Example: "Currently we use separate locations, but this is hard to manage..." + validations: + required: false + + - type: dropdown + id: impact + attributes: + label: Impact + description: How important is this feature for you? + options: + - Nice to have + - Important + - Critical / blocking our usage + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/02-bug.yml b/.github/ISSUE_TEMPLATE/02-bug.yml new file mode 100644 index 0000000..fd045c3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-bug.yml @@ -0,0 +1,125 @@ +name: Bug report +description: Report a problem in Defguard so we can reproduce and fix it. +title: "[Bug]: " +labels: + - bug +type: bug + +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. + + **Privacy note:** This issue tracker is public. Do not include sensitive data such as secrets, private keys, tokens, passwords, internal domains, or customer data. All data should be anonymised and any secrets must be redacted. + + - type: textarea + id: summary + attributes: + label: Summary + description: A clear, one-paragraph description of what’s broken. + placeholder: "After enabling MFA for a user, Desktop client cannot start a session; it loops on …" + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Numbered steps help us reproduce reliably. Attach screenshots or recordings if they help illustrate the steps. + placeholder: | + 1. Go to … + 2. Click … + 3. Configure … + 4. Observe … + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What should happen if the bug were fixed? + placeholder: "VPN connects successfully and a session is established." + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behavior + description: What happens instead? Include error messages, screenshots, or recordings if they help illustrate the issue. + placeholder: "VPN connection fails with … / UI shows … / API returns …" + validations: + required: true + + - type: input + id: version + attributes: + label: Defguard version + description: Exact versions of all components. + placeholder: "Core: v1.6.0, Gateway: v1.6.0, Edge: v1.6.0, Desktop client: v1.6.0, Mobile client: v1.6.0." + validations: + required: true + + - type: input + id: environment + attributes: + label: Environment details + description: Operating systems of all components. + placeholder: "Core: Ubuntu 24.04, Gateway: Ubuntu 24.04, Edge: Ubuntu 24.04, Desktop client: macOS 15.x, Mobile client: iOS 18." + validations: + required: true + + - type: dropdown + id: deployment + attributes: + label: Deployment / install method + description: How is Defguard installed? + multiple: false + options: + - One-line script + - Standalone packages + - Docker / Docker Compose + - Kubernetes / Helm + - Terraform + - AMI + - Custom + - Not installed (WireGuard only) + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant logs / output + description: Paste only what’s necessary. Please redact secrets (tokens, private IPs, domains) if needed. Enable DEBUG log level if you can (https://docs.defguard.net/support-1/how-to-submit-an-issue#id-1.-enable-debug-logging). + render: shell + placeholder: | + # Core logs + [2024-06-01T12:00:00Z] ERROR: ... + + # Edge logs + [2024-06-01T12:00:00Z] ERROR: ... + + # Gateway logs + [2024-06-01T12:00:00Z] ERROR: ... + + # Desktop client logs (you can find them in the app's settings) + [2024-06-01T12:00:00Z] ERROR: ... + + # Mobile client logs (you can find them in the app's main menu - View Application Logs) + [2024-06-01T12:00:00Z] ERROR: ... + validations: + required: false + + - type: textarea + id: config + attributes: + label: Relevant configuration (redacted) + description: If configuration is involved (LDAP, OIDC, WireGuard, gRPC certs), paste the minimum snippet. + render: yaml + placeholder: | + # Redact secrets/certs/private keys + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/03-internal.yml b/.github/ISSUE_TEMPLATE/03-internal.yml new file mode 100644 index 0000000..97278a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03-internal.yml @@ -0,0 +1,19 @@ +name: Internal issue +description: Internal Defguard team use only +labels: + - internal +type: task + +body: + - type: markdown + attributes: + value: | + This template is intended for internal Defguard team use. + + - type: textarea + id: description + attributes: + label: Description + placeholder: Detailed description, context, links, notes + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7987ea4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Defguard Troubleshooting Guide + url: https://docs.defguard.net/support-1/troubleshooting + about: Make sure to check the troubleshooting guide before submitting an issue. It has solutions for common problems and can help you debug faster. + - name: Open a discussion to get help from the community + url: https://github.com/DefGuard/defguard/discussions/new/choose + about: Having trouble with Defguard deployment or configuration? Reach out to our community for help. diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 172a7da..de22c52 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,13 +15,13 @@ on: jobs: build-ios: - runs-on: [self-hosted, macOS] + runs-on: [self-hosted, macOS, native] defaults: run: working-directory: ./client steps: - name: Checkout main repo - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: submodules: "recursive" @@ -29,7 +29,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.35.7 + flutter-version: 3.38.10 - name: Use homebrew ruby run: | @@ -40,7 +40,7 @@ jobs: run: flutter pub get - name: Unlock Keychain - run: security -v unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" /Users/admin/Library/Keychains/login.keychain + run: security unlock-keychain -p "${{ secrets.BUILD_KEYCHAIN_PASSWORD }}" build.keychain - name: Create BoringTun directory run: mkdir -p ios/VPNExtension/BoringTun @@ -55,27 +55,36 @@ jobs: run: flutter build ipa --release --obfuscate --split-debug-info=build/debug-info --build-number=${{ github.run_number }} - name: Upload app to TestFlight - uses: apple-actions/upload-testflight-build@v3 # Mobile applications are published to the App Store manually, with release tags applied # post-publication. To avoid redundant uploads, this step executes only for non-tagged # builds, ensuring tagged releases are distributed exclusively to GitHub. - if: "!startsWith(github.ref, 'refs/tags/')" - with: - app-path: "client/build/ios/ipa/Defguard.ipa" - issuer-id: ${{ secrets.API_ISSUER_ID }} - api-key-id: ${{ secrets.ASC_API_KEY_ID }} - api-private-key: ${{ secrets.PRIVATE_KEY_CONTENTS }} + run: | + xcrun altool --api-key ${{ secrets.ASC_API_KEY_ID }} \ + --api-issuer ${{ secrets.API_ISSUER_ID }} \ + --upload-app --platform ios --file build/ios/ipa/Defguard.ipa --wait - - name: Upload iOS Artifact - uses: actions/upload-artifact@v4 - if: startsWith(github.ref, 'refs/tags/') - with: - name: ios-app - path: "client/build/ios/ipa/Defguard.ipa" - retention-days: 2 + - name: Upload What's New + env: + APP_ID: "6748068630" + run: | + UPLOAD_DIR=$(mktemp -d) + VERSION=$(grep '^version:' pubspec.yaml | cut -d ' ' -f 2 | cut -d '+' -f 1) + mkdir -p "${UPLOAD_DIR}/beta-${APP_ID}/upload/IOS" + git log -1 --pretty='"whatsNew" = "%B";' > "${UPLOAD_DIR}/beta-${APP_ID}/upload/IOS/en-GB.txt" + RETRIES=0 + until [ ${RETRIES} -gt 6 ] + do + xcrun altool --api-key ${{ secrets.ASC_API_KEY_ID }} --api-issuer ${{ secrets.API_ISSUER_ID }} \ + --apple-id ${APP_ID} --bundle-version ${{ github.run_number }} \--bundle-short-version-string ${VERSION} \ + --platform macos --beta-app-store-text "${UPLOAD_DIR}" --upload && break + echo "Waiting for app ${APP_ID} build ${{ github.run_number }} version ${VERSION}" + sleep 10 + ((RETRIES++)) + done + rm -f -r "${UPLOAD_DIR}" build-android: - runs-on: [self-hosted, macOS] + runs-on: [self-hosted, macOS, native] env: ANDROID_HOME: /Users/admin/Library/Android/sdk ANDROID_SDK_ROOT: /Users/admin/Library/Android/sdk @@ -83,7 +92,9 @@ jobs: run: working-directory: ./client steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 + with: + submodules: "recursive" - name: Set up Java uses: actions/setup-java@v3 @@ -95,7 +106,10 @@ jobs: uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.32.7 + flutter-version: 3.38.10 + + - name: Configure Flutter JDK + run: flutter config --jdk-dir "$JAVA_HOME" - name: Accept licenses run: yes | flutter doctor --android-licenses @@ -110,6 +124,7 @@ jobs: run: flutter build appbundle --release --build-number=${{ github.run_number }} - name: Sign AAB + id: sign_aab uses: r0adkll/sign-android-release@v1 with: releaseDirectory: client/build/app/outputs/bundle/release @@ -127,21 +142,86 @@ jobs: with: serviceAccountJsonPlainText: "${{ secrets.ANDROID_SERVICE_ACCOUNT_JSON }}" packageName: net.defguard.mobile - releaseFiles: client/build/app/outputs/bundle/release/app-release.aab + releaseFiles: ${{ steps.sign_aab.outputs.signedReleaseFile }} track: internal + - name: Rename AAB + run: cp "$GITHUB_WORKSPACE/${{ steps.sign_aab.outputs.signedReleaseFile }}" Defguard.aab + - name: Upload Android Artifact uses: actions/upload-artifact@v4 if: startsWith(github.ref, 'refs/tags/') with: name: android-app - path: "client/build/app/outputs/bundle/release/app-release.aab" + path: "client/Defguard.aab" + retention-days: 2 + + build-android-apk: + runs-on: [self-hosted, macOS, native] + env: + ANDROID_HOME: /Users/admin/Library/Android/sdk + ANDROID_SDK_ROOT: /Users/admin/Library/Android/sdk + defaults: + run: + working-directory: ./client + steps: + - uses: actions/checkout@v7 + with: + submodules: "recursive" + + - name: Set up Java + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: "17" + + - name: Setup flutter + uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: 3.38.10 + + - name: Configure Flutter JDK + run: flutter config --jdk-dir "$JAVA_HOME" + + - name: Install Android SDK components + run: | + $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install 'build-tools;29.0.3' + - name: Accept licenses + run: yes | flutter doctor --android-licenses + + - name: Clean flutter + run: flutter clean + + - name: Install deps + run: flutter pub get + + - name: Build Android APK + run: flutter build apk --release --build-number=${{ github.run_number }} + + - name: Sign APK + id: sign_apk + uses: r0adkll/sign-android-release@v1 + with: + releaseDirectory: client/build/app/outputs/flutter-apk + signingKeyBase64: "${{ secrets.ANDROID_SIGNING_KEY_BASE64 }}" + alias: "${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}" + keyStorePassword: "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" + keyPassword: "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" + + - name: Rename APK + run: cp "$GITHUB_WORKSPACE/${{ steps.sign_apk.outputs.signedReleaseFile }}" Defguard.apk + + - name: Upload Android Artifact + uses: actions/upload-artifact@v4 + if: startsWith(github.ref, 'refs/tags/') + with: + name: android-app-apk + path: "client/Defguard.apk" retention-days: 2 release: - needs: [build-ios, build-android] + needs: [build-ios, build-android, build-android-apk] # Create release only if CI was triggered by a tag. if: startsWith(github.ref, 'refs/tags/') uses: ./.github/workflows/release.yaml - secrets: - PRIVATE_REPO_CLONING_TOKEN: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }} diff --git a/.github/workflows/lint-and-test.yaml b/.github/workflows/lint-and-test.yaml index 603d5d4..48a2de8 100644 --- a/.github/workflows/lint-and-test.yaml +++ b/.github/workflows/lint-and-test.yaml @@ -5,8 +5,8 @@ on: branches: - main - dev - - 'release/**' - - 'hotfix/**' + - "release/**" + - "hotfix/**" paths-ignore: &ignored_paths - "*.md" - "LICENSE" @@ -15,8 +15,8 @@ on: branches: - main - dev - - 'release/**' - - 'hotfix/**' + - "release/**" + - "hotfix/**" paths-ignore: *ignored_paths jobs: @@ -29,13 +29,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 + with: + submodules: "recursive" - name: Scan code with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@v0.36.0 with: - scan-type: 'fs' - scan-ref: '.' + scan-type: "fs" + scan-ref: "." exit-code: "1" ignore-unfixed: true severity: "CRITICAL,HIGH,MEDIUM" @@ -45,7 +47,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.32.4 + flutter-version: 3.38.10 - name: get deps run: flutter pub get @@ -55,7 +57,7 @@ jobs: # test-ios: # name: Run iOS tests - # runs-on: [self-hosted, macOS] + # runs-on: [self-hosted, macOS, native] # needs: lint # defaults: # run: @@ -63,16 +65,15 @@ jobs: # steps: # - name: Checkout - # uses: actions/checkout@v4 + # uses: actions/checkout@v7 # with: # submodules: "recursive" - # token: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }} # - name: setup flutter # uses: subosito/flutter-action@v2 # with: # channel: stable - # flutter-version: 3.32.6 + # flutter-version: 3.38.10 # - name: get deps # run: flutter pub get @@ -89,8 +90,5 @@ jobs: # # - name: build project # # run: flutter build ios - # - name: Unlock Keychain - # run: security -v unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" /Users/admin/Library/Keychains/login.keychain - # - name: run plugin tests # run: xcodebuild test -workspace Runner.xcworkspace -scheme Runner diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 410f794..a8d647f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -2,10 +2,6 @@ name: "Release" on: workflow_call: - secrets: - PRIVATE_REPO_CLONING_TOKEN: - description: "Cloning token" - required: true jobs: create-release: @@ -27,13 +23,11 @@ jobs: draft: true files: | ./artifacts/Defguard.ipa - ./artifacts/app-release.aab + ./artifacts/Defguard.aab + ./artifacts/Defguard.apk create-sbom: needs: [create-release] uses: ./.github/workflows/sbom.yaml with: upload_url: ${{ needs.create-release.outputs.upload_url }} - secrets: - PRIVATE_REPO_CLONING_TOKEN: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }} - diff --git a/.github/workflows/sbom-regenerate.yaml b/.github/workflows/sbom-regenerate.yaml index 3bd1f03..dff8a3c 100644 --- a/.github/workflows/sbom-regenerate.yaml +++ b/.github/workflows/sbom-regenerate.yaml @@ -1,8 +1,10 @@ name: Periodic SBOM Regeneration +permissions: + contents: write on: schedule: - - cron: '30 2 * * *' # 2:30 AM UTC + - cron: "30 2 * * *" # 2:30 AM UTC jobs: list-releases: @@ -35,5 +37,4 @@ jobs: with: upload_url: ${{ matrix.release.uploadUrl }} tag: ${{ matrix.release.tagName }} - secrets: - PRIVATE_REPO_CLONING_TOKEN: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }} + secrets: inherit diff --git a/.github/workflows/sbom.yaml b/.github/workflows/sbom.yaml index 99775f0..425331a 100644 --- a/.github/workflows/sbom.yaml +++ b/.github/workflows/sbom.yaml @@ -11,13 +11,11 @@ on: description: "The git tag to generate SBOM for - used in scheduled runs" required: false type: string - secrets: - PRIVATE_REPO_CLONING_TOKEN: - description: "Cloning token" - required: true jobs: create-sbom: + permissions: + contents: write runs-on: [self-hosted, Linux, X64] steps: @@ -31,29 +29,28 @@ jobs: echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: - submodules: recursive ref: ${{ steps.vars.outputs.TAG_NAME }} - token: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }} + submodules: "recursive" - name: Create SBOM with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@v0.36.0 with: - scan-type: 'fs' - format: 'spdx-json' + scan-type: "fs" + format: "spdx-json" output: "defguard-mobile-${{ steps.vars.outputs.VERSION }}.sbom.json" - scan-ref: '.' + scan-ref: "." severity: "CRITICAL,HIGH,MEDIUM,LOW" scanners: "vuln" - name: Create security advisory file with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@v0.36.0 with: - scan-type: 'fs' - format: 'json' + scan-type: "fs" + format: "json" output: "defguard-mobile-${{ steps.vars.outputs.VERSION }}.advisories.json" - scan-ref: '.' + scan-ref: "." severity: "CRITICAL,HIGH,MEDIUM,LOW" scanners: "vuln" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf7e962 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.envrc +.direnv/ + +# FVM Version Cache +.fvm/ diff --git a/README.md b/README.md index c166c55..6bf4e75 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,16 @@ The **DefGuard Mobile Client** is a secure, self-hosted **WireGuard VPN mobile c This open-source, cross-platform VPN app supports easy QR code onboarding and flexible traffic routing to meet diverse secure remote access needs. DefGuard is part of a modular ecosystem built for VPN orchestration and identity management . Defguard provides a mobile VPN client with biometrics and TOTP. ## Key Features -- Secure **WireGuard VPN mobile client** with **Multi-Factor Authentication (MFA)** -- Internal SSO/OIDC support with biometrics, TOTP, email verification -- External SSO support: Google, Okta, Microsoft EntraID, JumpCloud, and more -- Quick and easy onboarding via secure **QR code VPN onboarding** or URL/token -- Flexible traffic routing: all traffic via VPN or selective routing -- Real-time synchronization of VPN configurations with the DefGuard server -- Native **cross-platform VPN app** support for **Android VPN client** and iOS VPN client -- Fully **self-hosted VPN solution** for ultimate privacy and control -- Open-source codebase for transparency and customization + +- Secure **WireGuard VPN mobile client** with **Multi-Factor Authentication (MFA)** +- Internal SSO/OIDC support with biometrics, TOTP, email verification +- External SSO support: Google, Okta, Microsoft EntraID, JumpCloud, and more +- Quick and easy onboarding via secure **QR code VPN onboarding** or URL/token +- Flexible traffic routing: all traffic via VPN or selective routing +- Real-time synchronization of VPN configurations with the DefGuard server +- Native **cross-platform VPN app** support for **Android VPN client** and iOS VPN client +- Fully **self-hosted VPN solution** for ultimate privacy and control +- Open-source codebase for transparency and customization ## Screenshots @@ -24,24 +25,26 @@ This open-source, cross-platform VPN app supports easy QR code onboarding and fl Instance list MFA defguard IdP - ## Getting Started -You need to have a running [Defguard Server](https://github.com/DefGuard/defguard) to use the mobile app. +You need to have a running [Defguard Core](https://github.com/DefGuard/defguard) to use the mobile app. ### Install the App Join closed beta for iOS or Android. #### Android + - Download from [Google Play](https://play.google.com/store/apps/details?id=net.defguard.mobile) #### iOS -- Available soon on the [App Store](https://testflight.apple.com/join/Jvdhkt7h) -Documentation available at : [https://docs.defguard.net/help/mobile-client](https://docs.defguard.net/help/mobile-client) +- Available soon on the [App Store](https://apps.apple.com/us/app/defguard-vpn-client/id6748068630) + +Documentation available at : [https://docs.defguard.net/using-defguard-for-end-users/mobile-client](https://docs.defguard.net/using-defguard-for-end-users/mobile-client) ## About DefGuard -DefGuard is a comprehensive platform offering **secure remote access**, **identity management**, and VPN orchestration with a focus on security using **multi-factor authentication for VPN**. -Visit defguard.net for more information. \ No newline at end of file +DefGuard is a comprehensive platform offering **secure remote access**, **identity management**, and VPN orchestration with a focus on security using **multi-factor authentication for VPN**. + +Visit defguard.net for more information. diff --git a/client/.gitignore b/client/.gitignore index 72abeec..975efdc 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -50,5 +50,4 @@ app.*.map.json .envrc local.properties -ios/boringtun ios/VPNExtension/BoringTun diff --git a/client/README.md b/client/README.md index edc1edf..380db3c 100644 --- a/client/README.md +++ b/client/README.md @@ -1,10 +1,8 @@ -# mobile_client - -Defguard mobile client +# Defguard mobile client ## Getting Started -This project is a starting point for a Flutter application. +This is a Flutter application. A few resources to get you started if this is your first Flutter project: @@ -14,3 +12,25 @@ A few resources to get you started if this is your first Flutter project: For help getting started with Flutter development, view the [online documentation](https://docs.flutter.dev/), which offers tutorials, samples, guidance on mobile development, and a full API reference. + +## Database and migrations + +We use [drift](https://drift.simonbinder.eu/) persistence library with [SQLite](https://sqlite.org/index.html) +database. The model is defined in `lib/data/db/database.dart`. + +When changing the schema: + +1. Make changes to the model in `database.dart` file. +2. Bump schema version in `class AppDatabase`. +3. Generate migrations and tests: + +```bash +flutter pub run drift_dev make-migrations +``` + +4. Add your migration step to `AppDatabase` `onUpgrade`. +5. Run the tests: + +```bash +flutter test +``` diff --git a/client/android/app/build.gradle.kts b/client/android/app/build.gradle.kts index 3082919..760519f 100644 --- a/client/android/app/build.gradle.kts +++ b/client/android/app/build.gradle.kts @@ -8,7 +8,7 @@ plugins { android { namespace = "net.defguard.mobile" compileSdk = 36 - ndkVersion = "27.0.12077973" + ndkVersion = flutter.ndkVersion compileOptions { isCoreLibraryDesugaringEnabled = true @@ -31,12 +31,17 @@ android { buildTypes { release { // let r0adkll/sign-android-release@v1 in CI do the signing + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) } } } dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5") + implementation("com.google.android.gms:play-services-cronet:18.1.1") implementation(files("../../../lib/tunnel.aar")) } diff --git a/client/android/app/proguard-rules.pro b/client/android/app/proguard-rules.pro new file mode 100644 index 0000000..1d11401 --- /dev/null +++ b/client/android/app/proguard-rules.pro @@ -0,0 +1,3 @@ +# ProGuard rules for cronet_http +-keep class io.flutter.plugins.cronet_http.** { *; } +-keep class org.chromium.net.** { *; } diff --git a/client/android/app/src/main/AndroidManifest.xml b/client/android/app/src/main/AndroidManifest.xml index b602226..72aadc7 100644 --- a/client/android/app/src/main/AndroidManifest.xml +++ b/client/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ android:enableOnBackInvokedCallback="true" android:allowBackup="false" android:fullBackupContent="false" + android:networkSecurityConfig="@xml/network_security_config" android:dataExtractionRules="@xml/data_extraction_rules"> + + + + + + + + diff --git a/client/build.yaml b/client/build.yaml index bcfab5a..7fc2687 100644 --- a/client/build.yaml +++ b/client/build.yaml @@ -9,3 +9,7 @@ targets: create_factory: true create_to_json: true create_field_map: true + drift_dev: + options: + databases: + defguard: lib/data/db/database.dart diff --git a/client/drift_schemas/defguard/drift_schema_v1.json b/client/drift_schemas/defguard/drift_schema_v1.json new file mode 100644 index 0000000..df952fb --- /dev/null +++ b/client/drift_schemas/defguard/drift_schema_v1.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"defguard_instances","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"uuid","getter_name":"uuid","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"url","getter_name":"url","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"device_id","getter_name":"deviceId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"proxy_url","getter_name":"proxyUrl","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pooling_token","getter_name":"poolingToken","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"disable_all_traffic","getter_name":"disableAllTraffic","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"disable_all_traffic\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"disable_all_traffic\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"enterprise_enabled","getter_name":"enterpriseEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"enterprise_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"enterprise_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_keys_stored","getter_name":"mfaKeysStored","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_keys_stored\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_keys_stored\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[0],"type":"table","data":{"name":"locations","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"instance","getter_name":"instance","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES defguard_instances (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES defguard_instances (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"defguard_instances","column":"id"},"initially_deferred":false,"on_update":null,"on_delete":"cascade"}}]},{"name":"network_id","getter_name":"networkId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"address","getter_name":"address","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"endpoint","getter_name":"endpoint","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"allowed_ips","getter_name":"allowedIps","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"dns","getter_name":"dns","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_enabled","getter_name":"mfaEnabled","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"traffic_method","getter_name":"trafficMethod","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(RoutingMethod.values)","dart_type_name":"RoutingMethod"}},{"name":"mfa_method","getter_name":"mfaMethod","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MfaMethodConverter()","dart_type_name":"MfaMethod"}},{"name":"keep_alive_interval","getter_name":"keepAliveInterval","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"location_mfa_mode","getter_name":"locationMfaMode","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const LocationMfaModeConverter()","dart_type_name":"LocationMfaMode"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]} \ No newline at end of file diff --git a/client/drift_schemas/defguard/drift_schema_v2.json b/client/drift_schemas/defguard/drift_schema_v2.json new file mode 100644 index 0000000..2a2bb13 --- /dev/null +++ b/client/drift_schemas/defguard/drift_schema_v2.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"defguard_instances","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"uuid","getter_name":"uuid","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"url","getter_name":"url","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"device_id","getter_name":"deviceId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"proxy_url","getter_name":"proxyUrl","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pooling_token","getter_name":"poolingToken","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"client_traffic_policy","getter_name":"clientTrafficPolicy","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const ClientTrafficPolicyConverter()","dart_type_name":"ClientTrafficPolicy"}},{"name":"enterprise_enabled","getter_name":"enterpriseEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"enterprise_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"enterprise_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_keys_stored","getter_name":"mfaKeysStored","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_keys_stored\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_keys_stored\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[0],"type":"table","data":{"name":"locations","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"instance","getter_name":"instance","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES defguard_instances (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES defguard_instances (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"defguard_instances","column":"id"},"initially_deferred":false,"on_update":null,"on_delete":"cascade"}}]},{"name":"network_id","getter_name":"networkId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"address","getter_name":"address","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"endpoint","getter_name":"endpoint","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"allowed_ips","getter_name":"allowedIps","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"dns","getter_name":"dns","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_enabled","getter_name":"mfaEnabled","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"traffic_method","getter_name":"trafficMethod","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(RoutingMethod.values)","dart_type_name":"RoutingMethod"}},{"name":"mfa_method","getter_name":"mfaMethod","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MfaMethodConverter()","dart_type_name":"MfaMethod"}},{"name":"keep_alive_interval","getter_name":"keepAliveInterval","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"location_mfa_mode","getter_name":"locationMfaMode","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const LocationMfaModeConverter()","dart_type_name":"LocationMfaMode"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]} \ No newline at end of file diff --git a/client/drift_schemas/defguard/drift_schema_v3.json b/client/drift_schemas/defguard/drift_schema_v3.json new file mode 100644 index 0000000..ed25a86 --- /dev/null +++ b/client/drift_schemas/defguard/drift_schema_v3.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"defguard_instances","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"uuid","getter_name":"uuid","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"url","getter_name":"url","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"device_id","getter_name":"deviceId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"proxy_url","getter_name":"proxyUrl","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pooling_token","getter_name":"poolingToken","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"client_traffic_policy","getter_name":"clientTrafficPolicy","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const ClientTrafficPolicyConverter()","dart_type_name":"ClientTrafficPolicy"}},{"name":"enterprise_enabled","getter_name":"enterpriseEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"enterprise_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"enterprise_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_keys_stored","getter_name":"mfaKeysStored","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_keys_stored\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_keys_stored\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"openid_display_name","getter_name":"openidDisplayName","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[0],"type":"table","data":{"name":"locations","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"instance","getter_name":"instance","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES defguard_instances (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES defguard_instances (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"defguard_instances","column":"id"},"initially_deferred":false,"on_update":null,"on_delete":"cascade"}}]},{"name":"network_id","getter_name":"networkId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"address","getter_name":"address","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"endpoint","getter_name":"endpoint","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"allowed_ips","getter_name":"allowedIps","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"dns","getter_name":"dns","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_enabled","getter_name":"mfaEnabled","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"traffic_method","getter_name":"trafficMethod","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(RoutingMethod.values)","dart_type_name":"RoutingMethod"}},{"name":"mfa_method","getter_name":"mfaMethod","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MfaMethodConverter()","dart_type_name":"MfaMethod"}},{"name":"keep_alive_interval","getter_name":"keepAliveInterval","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"location_mfa_mode","getter_name":"locationMfaMode","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const LocationMfaModeConverter()","dart_type_name":"LocationMfaMode"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]} \ No newline at end of file diff --git a/client/drift_schemas/defguard/drift_schema_v4.json b/client/drift_schemas/defguard/drift_schema_v4.json new file mode 100644 index 0000000..22312c3 --- /dev/null +++ b/client/drift_schemas/defguard/drift_schema_v4.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"defguard_instances","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"uuid","getter_name":"uuid","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"url","getter_name":"url","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"device_id","getter_name":"deviceId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"proxy_url","getter_name":"proxyUrl","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pooling_token","getter_name":"poolingToken","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"client_traffic_policy","getter_name":"clientTrafficPolicy","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const ClientTrafficPolicyConverter()","dart_type_name":"ClientTrafficPolicy"}},{"name":"enterprise_enabled","getter_name":"enterpriseEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"enterprise_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"enterprise_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_keys_stored","getter_name":"mfaKeysStored","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_keys_stored\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_keys_stored\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"openid_display_name","getter_name":"openidDisplayName","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[0],"type":"table","data":{"name":"locations","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"instance","getter_name":"instance","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES defguard_instances (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES defguard_instances (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":[{"foreign_key":{"to":{"table":"defguard_instances","column":"id"},"initially_deferred":false,"on_update":null,"on_delete":"cascade"}}]},{"name":"network_id","getter_name":"networkId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"address","getter_name":"address","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"pub_key","getter_name":"pubKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"endpoint","getter_name":"endpoint","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"allowed_ips","getter_name":"allowedIps","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"dns","getter_name":"dns","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"mfa_enabled","getter_name":"mfaEnabled","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"mfa_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"mfa_enabled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"traffic_method","getter_name":"trafficMethod","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(RoutingMethod.values)","dart_type_name":"RoutingMethod"}},{"name":"mfa_method","getter_name":"mfaMethod","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MfaMethodConverter()","dart_type_name":"MfaMethod"}},{"name":"keep_alive_interval","getter_name":"keepAliveInterval","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"location_mfa_mode","getter_name":"locationMfaMode","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const LocationMfaModeConverter()","dart_type_name":"LocationMfaMode"}},{"name":"posture_check_required","getter_name":"postureCheckRequired","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"posture_check_required\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"posture_check_required\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]} \ No newline at end of file diff --git a/client/ios/Podfile.lock b/client/ios/Podfile.lock index 2126ffd..ce14a2c 100644 --- a/client/ios/Podfile.lock +++ b/client/ios/Podfile.lock @@ -18,10 +18,7 @@ PODS: - FlutterMacOS - package_info_plus (0.4.5): - Flutter - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - permission_handler_apple (9.3.0): + - permission_handler_apple (9.4.8): - Flutter - share_plus (0.0.1): - Flutter @@ -66,7 +63,6 @@ DEPENDENCIES: - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) @@ -97,8 +93,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/mobile_scanner/darwin" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" - path_provider_foundation: - :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" share_plus: @@ -119,16 +113,15 @@ SPEC CHECKSUMS: flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 - local_auth_darwin: fa4b06454df7df8e97c18d7ee55151c57e7af0de + local_auth_darwin: 63c73d6d28cc3e239be2b6aa460ea6e317cd5100 mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + permission_handler_apple: ee2fe0fd04551b304eb002714ff067c371c822ed share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b sqlite3_flutter_libs: 7bea6d85399aebaeb54e4f9845dcac6f5033cf22 - url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa wireguard_plugin: 4d2720563b180d23101f7162ecdbf57203e82e8e PODFILE CHECKSUM: ae9e65fc23486119b8e977fd41d9213f251e537a diff --git a/client/ios/Runner.xcodeproj/project.pbxproj b/client/ios/Runner.xcodeproj/project.pbxproj index 528e33b..39b0488 100644 --- a/client/ios/Runner.xcodeproj/project.pbxproj +++ b/client/ios/Runner.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 13AC128328D127CC1FFFF558 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B65B0851E892D176C6678C51 /* Pods_Runner.framework */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 284986622E1FAAA700BBCE47 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 287C99B22E1D2FA400965674 /* NetworkExtension.framework */; }; 287C99BB2E1D2FA400965674 /* VPNExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 287C99B12E1D2FA400965674 /* VPNExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -17,8 +18,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - D44F9D7BC923612123D748C1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D7C0FCE946457ECD05E164 /* Pods_RunnerTests.framework */; }; - FC45D341450122778AD42105 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CD43F789A53D9955D4328EF /* Pods_Runner.framework */; }; + DACB2DBAE80BDD7607EE4779 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EA0093455776E9C2E8600EF /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -53,28 +53,27 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 086F73C348A8C9390DD1F1F3 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 0CD8742B0F10B07586941587 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 201DFD21B12CFE608884216D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 2870EE742E20076800A83A9A /* wireguard_plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = wireguard_plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2870EE782E2008B400A83A9A /* wireguard_plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = wireguard_plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 287C99B12E1D2FA400965674 /* VPNExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = VPNExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 287C99B22E1D2FA400965674 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; 287C9A272E1D43DB00965674 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 2886A14D2E28ED64006A7931 /* MockVPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVPNManager.swift; sourceTree = ""; }; - 28DD48A42E3B5A7B008D3F6D /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = ""; }; 28F6C5EA2E1FD71200C01098 /* wireguard_plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = wireguard_plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2E3317479FFAF5BBC897DD53 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 2EA0093455776E9C2E8600EF /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4760374363F2DA4B1CE23DB5 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - 6C3E6D10E91648BACCEB24E2 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 6CD43F789A53D9955D4328EF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 424B254BB5DDF4D176DA0E7E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 5DA50AA8F82E69D4A7D35633 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 6E29205A5B56791C6A58AB8C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8DA37A462F22DF4069A30F0C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -82,8 +81,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - E7D7C0FCE946457ECD05E164 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EE706D7345143032DF760E81 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + B65B0851E892D176C6678C51 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BD3CFBCA806DBFB89630EE74 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -120,7 +119,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D44F9D7BC923612123D748C1 /* Pods_RunnerTests.framework in Frameworks */, + DACB2DBAE80BDD7607EE4779 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -128,7 +127,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - FC45D341450122778AD42105 /* Pods_Runner.framework in Frameworks */, + 13AC128328D127CC1FFFF558 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -181,7 +180,6 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 28DD48A42E3B5A7B008D3F6D /* RunnerDebug.entitlements */, 287C9A272E1D43DB00965674 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, @@ -202,8 +200,8 @@ 2870EE742E20076800A83A9A /* wireguard_plugin.framework */, 28F6C5EA2E1FD71200C01098 /* wireguard_plugin.framework */, 287C99B22E1D2FA400965674 /* NetworkExtension.framework */, - 6CD43F789A53D9955D4328EF /* Pods_Runner.framework */, - E7D7C0FCE946457ECD05E164 /* Pods_RunnerTests.framework */, + B65B0851E892D176C6678C51 /* Pods_Runner.framework */, + 2EA0093455776E9C2E8600EF /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -211,12 +209,12 @@ DE2536E81B1975928E1692FE /* Pods */ = { isa = PBXGroup; children = ( - 201DFD21B12CFE608884216D /* Pods-Runner.debug.xcconfig */, - 2E3317479FFAF5BBC897DD53 /* Pods-Runner.release.xcconfig */, - EE706D7345143032DF760E81 /* Pods-Runner.profile.xcconfig */, - 086F73C348A8C9390DD1F1F3 /* Pods-RunnerTests.debug.xcconfig */, - 6C3E6D10E91648BACCEB24E2 /* Pods-RunnerTests.release.xcconfig */, - 4760374363F2DA4B1CE23DB5 /* Pods-RunnerTests.profile.xcconfig */, + BD3CFBCA806DBFB89630EE74 /* Pods-Runner.debug.xcconfig */, + 6E29205A5B56791C6A58AB8C /* Pods-Runner.release.xcconfig */, + 5DA50AA8F82E69D4A7D35633 /* Pods-Runner.profile.xcconfig */, + 424B254BB5DDF4D176DA0E7E /* Pods-RunnerTests.debug.xcconfig */, + 8DA37A462F22DF4069A30F0C /* Pods-RunnerTests.release.xcconfig */, + 0CD8742B0F10B07586941587 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -248,7 +246,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 3CB5BDD8022927BDC0BBED95 /* [CP] Check Pods Manifest.lock */, + C42D5465F1F179E1EECF14FB /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 46C1966B05CC6B3688F00188 /* Frameworks */, @@ -267,15 +265,15 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 25F4A7C7AC197D56B91A98A9 /* [CP] Check Pods Manifest.lock */, + 12E55229B7D8012912191D7E /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 287C99BC2E1D2FA400965674 /* Embed Foundation Extensions */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - EF72AEB02D90D038422B97CE /* [CP] Embed Pods Frameworks */, - A2C99C1540BD0C64E164ABC6 /* [CP] Copy Pods Resources */, + BCC7D29F7E18EB3A195C9935 /* [CP] Embed Pods Frameworks */, + 7A28395B068B05A97BF2ECFE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -360,7 +358,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 25F4A7C7AC197D56B91A98A9 /* [CP] Check Pods Manifest.lock */ = { + 12E55229B7D8012912191D7E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -398,26 +396,21 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; - 3CB5BDD8022927BDC0BBED95 /* [CP] Check Pods Manifest.lock */ = { + 7A28395B068B05A97BF2ECFE /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { @@ -435,38 +428,43 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - A2C99C1540BD0C64E164ABC6 /* [CP] Copy Pods Resources */ = { + BCC7D29F7E18EB3A195C9935 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - EF72AEB02D90D038422B97CE /* [CP] Embed Pods Frameworks */ = { + C42D5465F1F179E1EECF14FB /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -563,6 +561,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 82GZ7KN29J; @@ -577,9 +576,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; + REGISTER_APP_GROUPS = YES; SUPPORTED_PLATFORMS = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -593,10 +592,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 82GZ7KN29J; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Defguard; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -606,10 +609,16 @@ "$(inherited)", "$(PROJECT_DIR)/VPNExtension/BoringTun", ); + MARKETING_VERSION = 1.7.0; PRODUCT_BUNDLE_IDENTIFIER = net.defguard.mobile; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -623,9 +632,11 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = VPNExtension/VPNExtensionDebug.entitlements; + CODE_SIGN_ENTITLEMENTS = VPNExtension/VPNExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 82GZ7KN29J; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -633,7 +644,6 @@ INFOPLIST_FILE = VPNExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = VPNExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -642,25 +652,24 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/VPNExtension/BoringTun", - "$(PROJECT_DIR)/VPNExtension/BoringTun-old", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.7.0; MODULEMAP_FILE = ""; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = net.defguard.mobile.VPNExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/VPNExtension/BoringTun"; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/VPNExtension/BoringTun/boringtunFFI.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/VPNExtension/BoringTun/defguard_boringtunFFI.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = ""; @@ -679,8 +688,10 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = VPNExtension/VPNExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 82GZ7KN29J; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -688,7 +699,6 @@ INFOPLIST_FILE = VPNExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = VPNExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -697,23 +707,22 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/VPNExtension/BoringTun", - "$(PROJECT_DIR)/VPNExtension/BoringTun-old", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.7.0; MODULEMAP_FILE = ""; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = net.defguard.mobile.VPNExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/VPNExtension/BoringTun"; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/VPNExtension/BoringTun/boringtunFFI.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/VPNExtension/BoringTun/defguard_boringtunFFI.h"; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = ""; TARGETED_DEVICE_FAMILY = "1,2"; @@ -731,8 +740,10 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = VPNExtension/VPNExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 82GZ7KN29J; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -740,7 +751,6 @@ INFOPLIST_FILE = VPNExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = VPNExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -749,23 +759,22 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/VPNExtension/BoringTun", - "$(PROJECT_DIR)/VPNExtension/BoringTun-old", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.7.0; MODULEMAP_FILE = ""; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = net.defguard.mobile.VPNExtension; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/VPNExtension/BoringTun"; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/VPNExtension/BoringTun/boringtunFFI.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/VPNExtension/BoringTun/defguard_boringtunFFI.h"; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = ""; TARGETED_DEVICE_FAMILY = "1,2"; @@ -775,7 +784,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 086F73C348A8C9390DD1F1F3 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 424B254BB5DDF4D176DA0E7E /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; @@ -795,7 +804,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6C3E6D10E91648BACCEB24E2 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 8DA37A462F22DF4069A30F0C /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; @@ -813,7 +822,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4760374363F2DA4B1CE23DB5 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 0CD8742B0F10B07586941587 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; @@ -860,6 +869,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 82GZ7KN29J; @@ -880,10 +890,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; + REGISTER_APP_GROUPS = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -919,6 +929,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 82GZ7KN29J; @@ -933,9 +944,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; + REGISTER_APP_GROUPS = YES; SUPPORTED_PLATFORMS = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; @@ -950,11 +961,15 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 82GZ7KN29J; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Defguard; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -964,11 +979,17 @@ "$(inherited)", "$(PROJECT_DIR)/boringtun/target/release", ); + MARKETING_VERSION = 1.7.0; PRODUCT_BUNDLE_IDENTIFIER = net.defguard.mobile; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -980,10 +1001,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 82GZ7KN29J; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Defguard; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -993,10 +1018,16 @@ "$(inherited)", "$(PROJECT_DIR)/boringtun/target/release", ); + MARKETING_VERSION = 1.7.0; PRODUCT_BUNDLE_IDENTIFIER = net.defguard.mobile; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/client/ios/Runner/RunnerDebug.entitlements b/client/ios/Runner/RunnerDebug.entitlements deleted file mode 100644 index 1e76d2f..0000000 --- a/client/ios/Runner/RunnerDebug.entitlements +++ /dev/null @@ -1,22 +0,0 @@ - - - - - com.apple.developer.networking.networkextension - - packet-tunnel-provider - - com.apple.developer.networking.vpn.api - - allow-vpn - - com.apple.security.app-sandbox - - com.apple.security.application-groups - - group.net.defguard.mobile - - keychain-access-groups - - - diff --git a/client/ios/VPNExtension/Adapter.swift b/client/ios/VPNExtension/Adapter.swift index 713fed9..103c163 100644 --- a/client/ios/VPNExtension/Adapter.swift +++ b/client/ios/VPNExtension/Adapter.swift @@ -1,7 +1,6 @@ import Foundation import Network import NetworkExtension -import os /// State of Adapter. enum State { @@ -13,7 +12,7 @@ enum State { case dormant } -final class Adapter /*: Sendable*/ { +@preconcurrency final class Adapter /*: Sendable*/ { /// Packet tunnel provider. private weak var packetTunnelProvider: NEPacketTunnelProvider? /// BortingTun tunnel @@ -25,17 +24,26 @@ final class Adapter /*: Sendable*/ { /// Network routes monitor. private var networkMonitor: NWPathMonitor? /// Keep alive timer - private var keepAliveTimer: Timer? - /// Logging - private lazy var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "Adapter") + private var keepAliveTimer: DispatchSourceTimer? + /// Unified logger (writes to both system log and file) + private let log = Log(category: "Adapter") /// Adapter state. private var state: State = .stopped - private var reconnectOnExpiry: Bool = false + /// Serialize tunnel I/O and connection state changes off the main queue. + private let ioQueue = DispatchQueue(label: "net.defguard.VPNExtension.adapter") + private let ioQueueKey = DispatchSpecificKey() + + /// For statistics returned to Rust code. + var locationId: UInt64? + var tunnelId: UInt64? + + private let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter() /// Designated initializer. /// - Parameter packetTunnelProvider: an instance of `NEPacketTunnelProvider`. Internally stored init(with packetTunnelProvider: NEPacketTunnelProvider) { self.packetTunnelProvider = packetTunnelProvider + self.ioQueue.setSpecific(key: ioQueueKey, value: ()) } deinit { @@ -43,8 +51,49 @@ final class Adapter /*: Sendable*/ { } func start(tunnelConfiguration: TunnelConfiguration) throws { - if let _ = tunnel { - logger.info("Cleaning exiting Tunnel") + try syncOnQueue { + try startOnQueue(tunnelConfiguration: tunnelConfiguration) + } + } + + func stop() { + syncOnQueue { + stopOnQueue() + } + } + + // Obtain tunnel statistics. + func stats() -> Stats? { + syncOnQueue { + guard let stats = tunnel?.stats() else { return nil } + return Stats( + txBytes: stats.txBytes, + rxBytes: stats.rxBytes, + lastHandshake: stats.lastHandshake, + locationId: locationId, + tunnelId: tunnelId + ) + } + } + + private func syncOnQueue(_ work: () throws -> T) rethrows -> T { + if DispatchQueue.getSpecific(key: ioQueueKey) == nil { + return try ioQueue.sync { + try work() + } + } + return try work() + } + + private func startOnQueue(tunnelConfiguration: TunnelConfiguration) throws { + guard case .stopped = self.state else { + log.error("Invalid state - cannot start tunnel") + // TODO: throw invalid state + return + } + + if tunnel != nil { + log.info("Cleaning existing Tunnel") tunnel = nil connection = nil } @@ -53,108 +102,102 @@ final class Adapter /*: Sendable*/ { networkMonitor.pathUpdateHandler = { [weak self] path in self?.networkPathUpdate(path: path) } - networkMonitor.start(queue: .main) + networkMonitor.start(queue: ioQueue) self.networkMonitor = networkMonitor - logger.info("Initializing Tunnel") + log.info("Initializing Tunnel") tunnel = try Tunnel.init( - privateKey: tunnelConfiguration.interface.privateKey, + privateKey: tunnelConfiguration.privateKey, serverPublicKey: tunnelConfiguration.peers[0].publicKey, presharedKey: tunnelConfiguration.peers[0].preSharedKey, keepAlive: tunnelConfiguration.peers[0].persistentKeepAlive, index: 0 ) + locationId = tunnelConfiguration.locationId + tunnelId = tunnelConfiguration.tunnelId - if tunnelConfiguration.peers[0].preSharedKey != nil { - logger.info("Using pre-shared key, the tunnel won't be re-established on expiry") - reconnectOnExpiry = false - } else { - logger.info("No pre-shared key, the tunnel will be re-established on expiry") - reconnectOnExpiry = true - } - - logger.info("Connecting to endpoint") + log.info( + "Connecting to endpoint (locationId: \(tunnelConfiguration.locationId ?? 0), tunnelId: \(tunnelConfiguration.tunnelId ?? 0))" + ) guard let endpoint = tunnelConfiguration.peers[0].endpoint else { - logger.error("Endpoint is nil") + log.error("Endpoint is nil, cannot connect") return } self.endpoint = endpoint.asNWEndpoint() initEndpoint() - logger.info("Sniffing packets") + log.info("Starting to sniff packets") readPackets() state = .running + log.info("Tunnel started successfully") } - func stop() { - logger.info("Stopping Adapter") + private func stopOnQueue() { + log.info("Stopping Adapter") connection?.cancel() connection = nil tunnel = nil - keepAliveTimer?.invalidate() + keepAliveTimer?.cancel() keepAliveTimer = nil // Cancel network monitor networkMonitor?.cancel() networkMonitor = nil + state = .stopped - logger.info("Tunnel stopped") + log.info("Tunnel stopped") + log.flush() } private func handleTunnelResult(_ result: TunnelResult) { + var tunnelPackets = [NEPacket]() + handleTunnelResult(result, tunnelPackets: &tunnelPackets) + flushTunnelPackets(tunnelPackets) + } + + private func handleTunnelResult(_ result: TunnelResult, tunnelPackets: inout [NEPacket]) { switch result { - case .done: - // Nothing to do. - break - case .err(let error): - logger.error("Tunnel error \(error, privacy: .public)") - switch error { - case .InvalidAeadTag: - logger.error("Invalid pre-shared key; stopping tunnel") - // The correct way is to call the packet tunnel provider, if there is one. - if let provider = packetTunnelProvider { - provider.cancelTunnelWithError(error) - } else { - stop() - } - case .ConnectionExpired: - packetTunnelProvider?.reasserting = true - if self.reconnectOnExpiry { - logger.error("Connecion has expired; re-connecting") - initEndpoint() - logger.info("Finished re-connecting") - } else { - logger.error("Connection has expired; stopping tunnel") - let defaults = UserDefaults(suiteName: suiteName) - defaults?.set( - TunnelStopError.mfaSessionExpired.rawValue - , forKey: "lastTunnelError") - if let provider = packetTunnelProvider { - provider.cancelTunnelWithError(error) - } else { - stop() - } - } - packetTunnelProvider?.reasserting = false - default: - break + case .done: + // Nothing to do. + break + case .err(let error): + log.error("Tunnel error: \(error)") + switch error { + case .InvalidAeadTag: + log.error("Invalid pre-shared key; stopping tunnel") + // The correct way is to call the packet tunnel provider, if there is one. + if let provider = packetTunnelProvider { + provider.cancelTunnelWithError(error) + } else { + stop() } - case .writeToNetwork(let data): - sendToEndpoint(data: data) - case .writeToTunnelV4(let data): - packetTunnelProvider?.packetFlow.writePacketObjects([ - NEPacket(data: data,protocolFamily: sa_family_t(AF_INET))]) - case .writeToTunnelV6(let data): - packetTunnelProvider?.packetFlow.writePacketObjects([ - NEPacket(data: data, protocolFamily: sa_family_t(AF_INET6))]) + case .ConnectionExpired: + log.warning("Connection has expired; re-connecting") + packetTunnelProvider?.reasserting = true + initEndpoint() + packetTunnelProvider?.reasserting = false + default: + break + } + case .writeToNetwork(let data): + sendToEndpoint(data: data) + case .writeToTunnelV4(let data): + tunnelPackets.append(NEPacket(data: data, protocolFamily: sa_family_t(AF_INET))) + case .writeToTunnelV6(let data): + tunnelPackets.append(NEPacket(data: data, protocolFamily: sa_family_t(AF_INET6))) } } + private func flushTunnelPackets(_ tunnelPackets: [NEPacket]) { + guard !tunnelPackets.isEmpty else { return } + packetTunnelProvider?.packetFlow.writePacketObjects(tunnelPackets) + } + /// Initialise UDP connection to endpoint. private func initEndpoint() { guard let endpoint = endpoint else { return } - logger.info("Init Endpoint") + log.info("Initializing endpoint connection to: \(endpoint)") // Cancel previous connection connection?.cancel() connection = nil @@ -166,43 +209,53 @@ final class Adapter /*: Sendable*/ { self?.endpointStateChange(state: state) } - connection.start(queue: .main) + connection.start(queue: ioQueue) self.connection = connection } /// Setup UDP connection to endpoint. This method should be called when UDP connection is ready to send and receive. private func setupEndpoint() { - logger.info("Setup endpoint") + log.info("Setting up endpoint") // Send initial handshake packet if let tunnel = self.tunnel { + log.info("Sending initial handshake") handleTunnelResult(tunnel.forceHandshake()) } - logger.info("Receiving UDP from endpoint") + log.info("Starting UDP receive loop") + log.debug("NWConnection path: \(String(describing: self.connection?.currentPath))") receive() - // Use Timer to send keep-alive packets. - keepAliveTimer?.invalidate() - logger.info("Creating keep-alive timer") - let timer = Timer(timeInterval: 0.25, repeats: true) { [weak self] timer in + // Use a dispatch timer to avoid bouncing keep-alives through the main run loop. + keepAliveTimer?.cancel() + log.info("Creating keep-alive timer") + let timer = DispatchSource.makeTimerSource(queue: ioQueue) + timer.schedule( + deadline: .now() + .seconds(1), + repeating: .seconds(1), + leeway: .milliseconds(25) + ) + timer.setEventHandler { [weak self] in guard let self = self, let tunnel = self.tunnel else { return } self.handleTunnelResult(tunnel.tick()) } keepAliveTimer = timer - RunLoop.main.add(timer, forMode: .common) + timer.resume() } /// Send packets to UDP endpoint. private func sendToEndpoint(data: Data) { guard let connection = connection else { return } if connection.state == .ready { - connection.send(content: data, completion: .contentProcessed { error in - if let error = error { - self.logger.error("UDP connection send error: \(error, privacy: .public)") - } - }) + connection.send( + content: data, + completion: .contentProcessed { [weak self] error in + if let error = error { + self?.log.error("UDP connection send error: \(error)") + } + }) } else { - logger.warning("UDP connection not ready to send") + log.warning("UDP connection not ready to send") } } @@ -211,60 +264,89 @@ final class Adapter /*: Sendable*/ { connection?.receiveMessage { [weak self] data, context, isComplete, error in guard let self = self else { return } if let data = data, let tunnel = self.tunnel { - self.handleTunnelResult(tunnel.read(src: data)) + autoreleasepool { + self.handleTunnelResult(tunnel.read(src: data)) + } } if error == nil { // continue receiving self.receive() + } else { + self.log.error("receive() error: \(String(describing: error))") } } } /// Read tunnel packets. private func readPackets() { + // Packets received to the tunnel's virtual interface. + packetTunnelProvider?.packetFlow.readPacketObjects { [weak self] packets in + guard let self = self else { return } + + self.ioQueue.async { + self.processTunnelPackets(packets) + + // continue reading + self.readPackets() + } + } + } + + private func processTunnelPackets(_ packets: [NEPacket]) { guard let tunnel = self.tunnel else { return } - // Packets received to the tunnel's virtual interface. - packetTunnelProvider?.packetFlow.readPacketObjects { packets in - for packet in packets { - self.handleTunnelResult(tunnel.write(src: packet.data)) + var tunnelPackets = [NEPacket]() + tunnelPackets.reserveCapacity(packets.count) + + for packet in packets { + autoreleasepool { + self.handleTunnelResult(tunnel.write(src: packet.data), tunnelPackets: &tunnelPackets) } - // continue reading - self.readPackets() } + + flushTunnelPackets(tunnelPackets) } /// Handle UDP connection state changes. private func endpointStateChange(state: NWConnection.State) { - logger.debug("UDP connection state: \(String(describing: state), privacy: .public)") + log.debug("UDP connection state changed: \(state)") switch state { - case .ready: - setupEndpoint() - case .failed(let error): - logger.error("Failed to establish endpoint connection: \(error)") - // The correct way is to call the packet tunnel provider, if there is one. - if let provider = packetTunnelProvider { - provider.cancelTunnelWithError(error) - } else { - stop() - } - default: - break + case .ready: + setupEndpoint() + //case .waiting(let error): + // switch error { + // case .posix(_): + // connection?.restart() + // default: + // self.stop() + // } + case .failed(let error): + log.error("Failed to establish endpoint connection: \(error)") + // The correct way is to call the packet tunnel provider, if there is one. + if let provider = packetTunnelProvider { + provider.cancelTunnelWithError(error) + } else { + stop() + } + default: + break } } /// Handle network path updates. private func networkPathUpdate(path: Network.NWPath) { + log.debug( + "Network path update - status: \(path.status), interfaces: \(path.availableInterfaces)") if path.status == .unsatisfied { if state == .running { - logger.warning("Unsatisfied network path: going dormant") + log.warning("Unsatisfied network path: going dormant") connection?.cancel() connection = nil state = .dormant } } else { if state == .dormant { - logger.warning("Satisfied network path: going running") + log.warning("Satisfied network path: going running") initEndpoint() state = .running } diff --git a/client/ios/VPNExtension/Decodabe+Encodable.swift b/client/ios/VPNExtension/Decodabe+Encodable.swift index bf01a54..d3f8a8f 100644 --- a/client/ios/VPNExtension/Decodabe+Encodable.swift +++ b/client/ios/VPNExtension/Decodabe+Encodable.swift @@ -2,7 +2,7 @@ import Foundation extension Decodable { static func from(dictionary: [String: Any]) throws -> Self { - let data = try JSONSerialization.data(withJSONObject: dictionary, options: []) + let data = try JSONSerialization.data(withJSONObject: dictionary) let decoder = JSONDecoder() return try decoder.decode(Self.self, from: data) } @@ -13,7 +13,9 @@ extension Encodable { let data = try JSONEncoder().encode(self) let jsonObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) guard let dictionary = jsonObject as? [String: Any] else { - throw NSError(domain: "EncodingError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to convert to dictionary"]) + throw NSError( + domain: "EncodingError", code: 0, + userInfo: [NSLocalizedDescriptionKey: "Failed to convert to dictionary"]) } return dictionary } diff --git a/client/ios/VPNExtension/Endpoint.swift b/client/ios/VPNExtension/Endpoint.swift index 790aee3..64f50a4 100644 --- a/client/ios/VPNExtension/Endpoint.swift +++ b/client/ios/VPNExtension/Endpoint.swift @@ -1,3 +1,4 @@ +import Foundation import Network struct Endpoint: Codable, CustomStringConvertible { @@ -15,9 +16,11 @@ struct Endpoint: Codable, CustomStringConvertible { var endpointHost = trimmedEndpoint // Extract host, supporting IPv4, IPv6, and domains - if trimmedEndpoint.hasPrefix("[") { // IPv6 with port, e.g. [fd00::1]:51820 + if trimmedEndpoint.hasPrefix("[") { // IPv6 with port, e.g. [fd00::1]:51820 if let closing = trimmedEndpoint.firstIndex(of: "]") { - endpointHost = String(trimmedEndpoint[trimmedEndpoint.index(after: trimmedEndpoint.startIndex).. String { + "\(host):\(port)" } + // Encode to a single string "host:port", to smoothly encode into JSON. func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode("\(host)", forKey: .host) - try container.encode(port.rawValue, forKey: .port) + var container = encoder.singleValueContainer() + try container.encode(self.toString()) } + // Decode from a single string "host:port", to smoothly decode from JSON. init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - - host = try NWEndpoint.Host(values.decode(String.self, forKey: .host)) - port = try NWEndpoint.Port(rawValue: values.decode(UInt16.self, forKey: .port)) ?? 0 + let container = try decoder.singleValueContainer() + let value = try container.decode(String.self) + guard let endpoint = Endpoint(from: value) else { + throw + DecodingError + .dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Not in host:port format") + ) + } + self = endpoint } func asNWEndpoint() -> NWEndpoint { diff --git a/client/ios/VPNExtension/FileLogger.swift b/client/ios/VPNExtension/FileLogger.swift new file mode 100644 index 0000000..71d0297 --- /dev/null +++ b/client/ios/VPNExtension/FileLogger.swift @@ -0,0 +1,251 @@ +import Foundation +import os + +/// Log levels +enum LogLevel: String { + case debug = "DEBUG" + case info = "INFO" + case warning = "WARN" + case error = "ERROR" + + var osLogType: OSLogType { + switch self { + case .debug: return .debug + case .info: return .info + case .warning: return .default + case .error: return .error + } + } +} + +/// Logger that writes to both system log (os.Logger) and file. +/// Use this instead of os.Logger directly to get dual logging with a single call. +final class Log { + /// The category for this logger instance (usually class name), e.g. "PacketTunnelProvider" + let category: String + private let systemLogger: Logger +#if os(macOS) + private let fileLogger = FileLogger.shared +#endif + + init(category: String) { + self.category = category + self.systemLogger = Logger( + subsystem: Bundle.main.bundleIdentifier ?? "net.defguard.VPNExtension", + category: category + ) + } + + func debug(_ message: String) { + systemLogger.debug("\(message, privacy: .public)") +#if os(macOS) + fileLogger.log(level: .debug, message: message, category: category) +#endif + } + + func info(_ message: String) { + systemLogger.info("\(message, privacy: .public)") +#if os(macOS) + fileLogger.log(level: .info, message: message, category: category) +#endif + } + + func warning(_ message: String) { + systemLogger.warning("\(message, privacy: .public)") +#if os(macOS) + fileLogger.log(level: .warning, message: message, category: category) +#endif + } + + func error(_ message: String) { + systemLogger.error("\(message, privacy: .public)") +#if os(macOS) + fileLogger.log(level: .error, message: message, category: category) +#endif + } + + func flush() { +#if os(macOS) + fileLogger.flush() +#endif + } +} + +#if os(macOS) +/// A file-based logger that writes to an App Group shared container. +/// This allows the main rust app to read logs from the network extension. +/// Use the `Log` class instead of this directly for unified logging. +final class FileLogger { + static let shared = FileLogger() + static let appGroupIdentifier = "group.net.defguard" + private let logFileName = "vpn-extension.log" + private let maxLogFileSize: UInt64 = 5 * 1024 * 1024 // 5 MB + private let maxBackupFiles = 3 + private let flushInterval = 5 // Flush every N log entries + private var fileHandle: FileHandle? + private var logFileURL: URL? + private var unflushedCount = 0 + + private let dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(identifier: "UTC") + return formatter + }() + + private let queue = DispatchQueue(label: "net.defguard.VPNExtension.filelogger") + + private let internalLogger = Logger( + subsystem: Bundle.main.bundleIdentifier ?? "net.defguard.VPNExtension", + category: "FileLogger") + + private init() { + setupLogFile() + } + + deinit { + closeLogFile() + } + + private func setupLogFile() { + guard + let containerURL = FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: Self.appGroupIdentifier) + else { + internalLogger.error( + "Failed to get App Group container URL for \(Self.appGroupIdentifier)") + return + } + + let logsDirectory = containerURL.appendingPathComponent("Logs", isDirectory: true) + + do { + try FileManager.default.createDirectory( + at: logsDirectory, withIntermediateDirectories: true, attributes: nil) + } catch { + internalLogger.error("Failed to create Logs directory: \(error.localizedDescription)") + return + } + + logFileURL = logsDirectory.appendingPathComponent(logFileName) + + guard let logFileURL = logFileURL else { return } + + if !FileManager.default.fileExists(atPath: logFileURL.path) { + FileManager.default.createFile(atPath: logFileURL.path, contents: nil, attributes: nil) + } + + do { + fileHandle = try FileHandle(forWritingTo: logFileURL) + fileHandle?.seekToEndOfFile() + + let startupMessage = + "# VPN Extension Log Started at \(dateFormatter.string(from: Date()))\n" + if let data = startupMessage.data(using: .utf8) { + fileHandle?.write(data) + } + } catch { + internalLogger.error( + "Failed to open log file for writing: \(error.localizedDescription)") + } + + internalLogger.info("FileLogger initialized at: \(logFileURL.path)") + } + + private func closeLogFile() { + queue.sync { + try? fileHandle?.synchronize() + try? fileHandle?.close() + fileHandle = nil + } + } + + /// Rotate log files if the current one exceeds the maximum size + private func rotateLogFilesIfNeeded() { + guard let logFileURL = logFileURL else { return } + + do { + let attributes = try FileManager.default.attributesOfItem(atPath: logFileURL.path) + if let fileSize = attributes[.size] as? UInt64, fileSize >= maxLogFileSize { + rotateLogFiles() + } + } catch { + } + } + + private func rotateLogFiles() { + guard let logFileURL = logFileURL else { return } + + try? fileHandle?.synchronize() + try? fileHandle?.close() + fileHandle = nil + + let fileManager = FileManager.default + let directory = logFileURL.deletingLastPathComponent() + let baseName = logFileURL.deletingPathExtension().lastPathComponent + let ext = logFileURL.pathExtension + + // Remove oldest backup if it exists + let oldestBackup = directory.appendingPathComponent("\(baseName).\(maxBackupFiles).\(ext)") + try? fileManager.removeItem(at: oldestBackup) + + for i in stride(from: maxBackupFiles - 1, through: 1, by: -1) { + let current = directory.appendingPathComponent("\(baseName).\(i).\(ext)") + let next = directory.appendingPathComponent("\(baseName).\(i + 1).\(ext)") + try? fileManager.moveItem(at: current, to: next) + } + + let firstBackup = directory.appendingPathComponent("\(baseName).1.\(ext)") + try? fileManager.moveItem(at: logFileURL, to: firstBackup) + + fileManager.createFile(atPath: logFileURL.path, contents: nil, attributes: nil) + + do { + fileHandle = try FileHandle(forWritingTo: logFileURL) + fileHandle?.seekToEndOfFile() + } catch { + internalLogger.error( + "Failed to reopen log file after rotation: \(error.localizedDescription)") + } + } + + /// Write a log message to the file + /// - level: Log level (debug, info, warning, error) + /// - message: The message to log + /// - category: Optional category/subsystem + func log(level: LogLevel, message: String, category: String? = nil) { + queue.async { [weak self] in + guard let self = self, let fileHandle = self.fileHandle else { return } + + self.rotateLogFilesIfNeeded() + + let timestamp = self.dateFormatter.string(from: Date()) + let categoryStr = category.map { "[\($0)] " } ?? "" + let logLine = "\(timestamp) [\(level.rawValue)] \(categoryStr)\(message)\n" + + if let data = logLine.data(using: .utf8) { + fileHandle.write(data) + self.unflushedCount += 1 + + // Flush for important messages or periodically + if level == .error || level == .warning || self.unflushedCount >= self.flushInterval + { + try? fileHandle.synchronize() + self.unflushedCount = 0 + } + } + } + } + + func flush() { + queue.sync { + try? fileHandle?.synchronize() + } + } + + var logFilePath: String? { + return logFileURL?.path + } +} +#endif diff --git a/client/ios/VPNExtension/InterfaceConfiguration.swift b/client/ios/VPNExtension/InterfaceConfiguration.swift deleted file mode 100644 index c73f539..0000000 --- a/client/ios/VPNExtension/InterfaceConfiguration.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation -import NetworkExtension - -final class InterfaceConfiguration: Codable { - var privateKey: String - var addresses: [IpAddrMask] = [] - var listenPort: UInt16? - var mtu: UInt32? - var dns: [String] = [] - var dnsSearch: [String] = [] - - init(privateKey: String) { - self.privateKey = privateKey - } -} diff --git a/client/ios/VPNExtension/IpAddrMask.swift b/client/ios/VPNExtension/IpAddrMask.swift index 53a0a56..fefd845 100644 --- a/client/ios/VPNExtension/IpAddrMask.swift +++ b/client/ios/VPNExtension/IpAddrMask.swift @@ -15,17 +15,20 @@ struct IpAddrMask: Codable, Equatable { separator: "/", maxSplits: 1, ) + let default_cidr: UInt8 if let ipv4 = IPv4Address(String(parts[0])) { address = ipv4 + default_cidr = 32 } else if let ipv6 = IPv6Address(String(parts[0])) { address = ipv6 + default_cidr = 128 } else { return nil } if parts.count > 1 { cidr = UInt8(parts[1]) ?? 0 } else { - cidr = 0 + cidr = default_cidr } } @@ -41,7 +44,7 @@ struct IpAddrMask: Codable, Equatable { /// Conform to `Encodable`. func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(address.rawValue, forKey: .address) + try container.encode("\(address)", forKey: .address) try container.encode(cidr, forKey: .cidr) } @@ -49,33 +52,21 @@ struct IpAddrMask: Codable, Equatable { init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - let address_data = try values.decode(Data.self, forKey: .address) - switch address_data.count { - case 4: - guard let ipv4 = IPv4Address(address_data) else { - throw DecodingError - .dataCorrupted(DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Unable to decode IP v4 address" - )) - - } - address = ipv4 - case 16: - guard let ipv6 = IPv6Address(address_data) else { - throw DecodingError - .dataCorrupted(DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Unable to decode IP v6 address" - )) - - } - address = ipv6 - default: - throw DecodingError.typeMismatch(IpAddrMask.self, DecodingError.Context( - codingPath: decoder.codingPath, debugDescription: "Invalid IP address length" - )) + let address_string = try values.decode(String.self, forKey: .address) + if let ipv4 = IPv4Address(address_string) { + address = ipv4 + } else if let ipv6 = IPv6Address(address_string) { + address = ipv6 + } else { + throw + DecodingError + .dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Unable to decode IP address" + )) } + cidr = try values.decode(UInt8.self, forKey: .cidr) } @@ -96,17 +87,18 @@ struct IpAddrMask: Codable, Equatable { // Note: UInt128 is available since iOS 18. Use UInt64 implementation. if address is IPv6Address { var bytes = Data(count: 16) - let (mask_upper, mask_lower) = if cidr < 64 { - ( - cidr == 0 ? UInt64.min : UInt64.max << (64 - cidr), - UInt64.min - ) - } else { - ( - UInt64.max, - (cidr - 64) == 0 ? UInt64.min : UInt64.max << (128 - cidr) - ) - } + let (mask_upper, mask_lower) = + if cidr < 64 { + ( + cidr == 0 ? UInt64.min : UInt64.max << (64 - cidr), + UInt64.min + ) + } else { + ( + UInt64.max, + (cidr - 64) == 0 ? UInt64.min : UInt64.max << (128 - cidr) + ) + } for i in 0...7 { bytes[i] = UInt8(truncatingIfNeeded: mask_upper >> (56 - i * 8)) } @@ -117,4 +109,23 @@ struct IpAddrMask: Codable, Equatable { } fatalError() } + + /// Return address with the mask applied. + func maskedAddress() -> IPAddress { + let subnet = mask().rawValue + var masked = Data(address.rawValue) + if subnet.count != masked.count { + fatalError() + } + for i in 0.. Void) { - guard let tunnelConfig = extractTunnelConfiguration() else { - let error = NSError(domain: "VPNExtension", code: -1, - userInfo: [NSLocalizedDescriptionKey: "Tunnel configuration is missing or invalid."]) - logger.error("Tunnel configuration is missing or invalid.") - completionHandler(error) - return + override func startTunnel( + options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void + ) { + if let options = options { + log.debug("Options: \(options)") } - logger.log("Starting tunnel with configuration: \(String(describing: tunnelConfig), privacy: .public)") + guard let protocolConfig = self.protocolConfiguration as? NETunnelProviderProtocol, + let providerConfig = protocolConfig.providerConfiguration + else { + log.error("Failed to parse provider configuration") + completionHandler(WireGuardTunnelError.invalidTunnelConfiguration) + return + } - guard Endpoint(from: tunnelConfig.endpoint) != nil else { - let error = NSError(domain: "VPNExtension", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid endpoint format: \(tunnelConfig.endpoint)"]) - logger.error("Invalid endpoint format: \(tunnelConfig.endpoint, privacy: .public)") - completionHandler(error) +#if os(macOS) + guard let tunnelConfig = try? TunnelConfiguration.from(dictionary: providerConfig) + else { + log.error("Failed to parse tunnel configuration") + completionHandler(WireGuardTunnelError.invalidTunnelConfiguration) return } +#else + guard let startData = try? TunnelStartData.from(dictionary: providerConfig) + else { + log.error("Failed to parse tunnel configuration") + completionHandler(WireGuardTunnelError.invalidTunnelConfiguration) + return + } + let tunnelConfig = TunnelConfiguration(fromStartData: startData) +#endif - let tunnelConfiguration = TunnelConfiguration(fromStartData: tunnelConfig) - let networkSettings = tunnelConfiguration.asNetworkSettings() - - setTunnelNetworkSettings(networkSettings) { [weak self] error in - guard let self = self else { return } - - if let error = error { - logger.warning("Set tunnel network settings returned an error \(error, privacy: .public)") - completionHandler(error) - return - } - - do { - try self.adapter.start(tunnelConfiguration: tunnelConfiguration) - } catch { - logger.error("Failed to start adapter with error: \(error.localizedDescription, privacy: .public)") - completionHandler(error) - return + let networkSettings = tunnelConfig.asNetworkSettings() + self.setTunnelNetworkSettings(networkSettings) { error in + if error != nil { + self.log.error("Failed to set tunnel network settings: \(String(describing: error))") } + completionHandler(error) + return + } - logger.log("Tunnel started successfully") - completionHandler(nil) + do { + try adapter.start(tunnelConfiguration: tunnelConfig) + } catch { + log.error("Failed to start tunnel: \(error)") + completionHandler(error) } + log.info("Tunnel started successfully") + + completionHandler(nil) } - override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { - self.adapter.stop() + override func stopTunnel( + with reason: NEProviderStopReason, completionHandler: @escaping () -> Void + ) { + adapter.stop() + log.info("Tunnel stopped") completionHandler() } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { - logger.debug("\(#function)") + // TODO: messageData should contain a valid message. if let handler = completionHandler { - handler(messageData) + if let stats = adapter.stats() { + let data = try? JSONEncoder().encode(stats) + handler(data) + } else { + handler(nil) + } } } override func sleep(completionHandler: @escaping () -> Void) { - logger.debug("\(#function)") + log.info("System going to sleep") + // Add code here to get ready to sleep. completionHandler() } override func wake() { - logger.debug("\(#function)") - } - - // MARK: - Helpers - - private func extractTunnelConfiguration() -> TunnelStartData? { - guard let providerConfig = (self.protocolConfiguration as? NETunnelProviderProtocol)?.providerConfiguration as? [String: Any] else { - return nil - } - return try? TunnelStartData.from(dictionary: providerConfig) + log.info("System waking up") + // Add code here to wake up. } } diff --git a/client/ios/VPNExtension/Peer.swift b/client/ios/VPNExtension/Peer.swift index e30b61b..fc07220 100644 --- a/client/ios/VPNExtension/Peer.swift +++ b/client/ios/VPNExtension/Peer.swift @@ -4,23 +4,26 @@ final class Peer: Codable { var publicKey: String var preSharedKey: String? var endpoint: Endpoint? + var persistentKeepAlive: UInt16? + var allowedIPs = [IpAddrMask]() + // Statistics var lastHandshake: Date? var txBytes: UInt64 = 0 var rxBytes: UInt64 = 0 - var persistentKeepAlive: UInt16? - var allowedIPs = [IpAddrMask]() - init(publicKey: String, preSharedKey: String? = nil, endpoint: Endpoint? = nil, - lastHandshake: Date? = nil, txBytes: UInt64 = 0, rxBytes: UInt64 = 0, - persistentKeepAlive: UInt16? = nil, allowedIPs: [IpAddrMask] = [IpAddrMask]()) { + init( + publicKey: String, preSharedKey: String? = nil, endpoint: Endpoint? = nil, + persistentKeepAlive: UInt16? = nil, allowedIPs: [IpAddrMask] = [IpAddrMask](), + lastHandshake: Date? = nil, txBytes: UInt64 = 0, rxBytes: UInt64 = 0, + ) { self.publicKey = publicKey self.preSharedKey = preSharedKey self.endpoint = endpoint + self.persistentKeepAlive = persistentKeepAlive + self.allowedIPs = allowedIPs self.lastHandshake = lastHandshake self.txBytes = txBytes self.rxBytes = rxBytes - self.persistentKeepAlive = persistentKeepAlive - self.allowedIPs = allowedIPs } init(publicKey: String) { @@ -31,10 +34,11 @@ final class Peer: Codable { case publicKey case preSharedKey case endpoint - case lastHandshake - case txBytes - case rxBytes case persistentKeepAlive case allowedIPs + // There isn't any need to encode/decode these ephemeral fields. + // case lastHandshake + // case txBytes + // case rxBytes } } diff --git a/client/ios/VPNExtension/Stats.swift b/client/ios/VPNExtension/Stats.swift new file mode 100644 index 0000000..2c83344 --- /dev/null +++ b/client/ios/VPNExtension/Stats.swift @@ -0,0 +1,18 @@ +import ObjectiveC + +public class Stats: NSObject, Codable { + var txBytes: UInt64 + var rxBytes: UInt64 + var lastHandshake: UInt64 + // One or the other. + var locationId: UInt64? + var tunnelId: UInt64? + + init(txBytes: UInt64, rxBytes: UInt64, lastHandshake: UInt64, locationId: UInt64?, tunnelId: UInt64?) { + self.txBytes = txBytes + self.rxBytes = rxBytes + self.lastHandshake = lastHandshake + self.locationId = locationId + self.tunnelId = tunnelId + } +} diff --git a/client/ios/VPNExtension/TunnelConfiguration.swift b/client/ios/VPNExtension/TunnelConfiguration.swift index 73b28f6..619a17c 100644 --- a/client/ios/VPNExtension/TunnelConfiguration.swift +++ b/client/ios/VPNExtension/TunnelConfiguration.swift @@ -2,14 +2,23 @@ import Foundation import NetworkExtension final class TunnelConfiguration: Codable { - var name: String - var interface: InterfaceConfiguration - var peers: [Peer] + // One or the other. + var locationId: UInt64? + var tunnelId: UInt64? - init(name: String, interface: InterfaceConfiguration, peers: [Peer]) { - self.interface = interface - self.peers = peers + var name: String + var privateKey: String + var addresses: [IpAddrMask] = [] + var listenPort: UInt16? + var peers: [Peer] = [] + var mtu: UInt32? + var dns: [String] = [] + var dnsSearch: [String] = [] + + init(name: String, privateKey: String, peers: [Peer]) { self.name = name + self.privateKey = privateKey + self.peers = peers let peerPublicKeysArray = peers.map { $0.publicKey } let peerPublicKeysSet = Set(peerPublicKeysArray) @@ -18,58 +27,116 @@ final class TunnelConfiguration: Codable { } } - // Only encode these properties. + /// Only encode these properties. enum CodingKeys: String, CodingKey { + case locationId + case tunnelId case name - case interface + case privateKey + case addresses + case listenPort case peers + case mtu + case dns + case dnsSearch } func asNetworkSettings() -> NEPacketTunnelNetworkSettings { // Keep 127.0.0.1 as remote address for WireGuard. let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") + let (ipv4IncludedRoutes, ipv6IncludedRoutes) = routes() + // IPv4 addresses - let addrs_v4 = interface.addresses.filter { $0.address is IPv4Address } + let addrs_v4 = addresses.filter { $0.address is IPv4Address } .map { String(describing: $0.address) } - let masks_v4 = interface.addresses.filter { $0.address is IPv4Address } + let masks_v4 = addresses.filter { $0.address is IPv4Address } .map { String(describing: $0.mask()) } let ipv4Settings = NEIPv4Settings(addresses: addrs_v4, subnetMasks: masks_v4) - ipv4Settings.includedRoutes = peers[0].allowedIPs.filter { $0.address is IPv4Address }.map { - NEIPv4Route(destinationAddress: "\($0.address)", subnetMask: "\($0.mask())") - } + ipv4Settings.includedRoutes = ipv4IncludedRoutes networkSettings.ipv4Settings = ipv4Settings // IPv6 addresses - let addrs_v6 = interface.addresses.filter { $0.address is IPv6Address } + let addrs_v6 = addresses.filter { $0.address is IPv6Address } .map { String(describing: $0.address) } - let masks_v6 = interface.addresses.filter { $0.address is IPv6Address } - .map { NSNumber(value: $0.cidr) } + // IMPORTANT: macOS/iOS has limitations handling IPv6 prefix masks longer than /120 due to + // standards compliance and implementation choices in its network stack. + let masks_v6 = addresses.filter { $0.address is IPv6Address } + .map { NSNumber(value: min(120, $0.cidr)) } let ipv6Settings = NEIPv6Settings(addresses: addrs_v6, networkPrefixLengths: masks_v6) - ipv6Settings.includedRoutes = peers[0].allowedIPs.filter { $0.address is IPv6Address }.map { - NEIPv6Route(destinationAddress: "\($0.address)", - networkPrefixLength: NSNumber(value: $0.cidr)) - } + ipv6Settings.includedRoutes = ipv6IncludedRoutes networkSettings.ipv6Settings = ipv6Settings - networkSettings.mtu = interface.mtu as NSNumber? + networkSettings.mtu = mtu as NSNumber? networkSettings.tunnelOverheadBytes = 80 - let dnsSettings = NEDNSSettings(servers: interface.dns) - dnsSettings.searchDomains = interface.dnsSearch - if !interface.dns.isEmpty { - // Make all DNS queries go through the tunnel. - dnsSettings.matchDomains = [""] + + let dnsSettings = NEDNSSettings(servers: dns) + if !dns.isEmpty { + if dnsSearch.isEmpty { + // Resolve all DNS queries. + dnsSettings.matchDomains = [""] + } else { + // Split DNS queries. + dnsSettings.matchDomains = dnsSearch + dnsSettings.searchDomains = dnsSearch + } } networkSettings.dnsSettings = dnsSettings return networkSettings } + /// Return array of routes for IPv4 and IPv6. + private func routes() -> ([NEIPv4Route], [NEIPv6Route]) { + var ipv4IncludedRoutes = [NEIPv4Route]() + var ipv6IncludedRoutes = [NEIPv6Route]() + + // Routes to interface addresses. + for addr_mask in addresses { + if addr_mask.address is IPv4Address { + let route = NEIPv4Route( + destinationAddress: "\(addr_mask.maskedAddress())", + subnetMask: "\(addr_mask.mask())") + route.gatewayAddress = "\(addr_mask.address)" + ipv4IncludedRoutes.append(route) + } else if addr_mask.address is IPv6Address { + let route = NEIPv6Route( + destinationAddress: "\(addr_mask.maskedAddress())", + networkPrefixLength: NSNumber(value: addr_mask.cidr) + ) + route.gatewayAddress = "\(addr_mask.address)" + ipv6IncludedRoutes.append(route) + } + } + + // Routes to peer's allowed IPs. + for peer in peers { + for addr_mask in peer.allowedIPs { + if addr_mask.address is IPv4Address { + ipv4IncludedRoutes.append( + NEIPv4Route( + destinationAddress: "\(addr_mask.address)", + subnetMask: "\(addr_mask.mask())")) + } else if addr_mask.address is IPv6Address { + ipv6IncludedRoutes.append( + NEIPv6Route( + destinationAddress: "\(addr_mask.address)", + networkPrefixLength: NSNumber(value: addr_mask.cidr))) + } + } + } + + return (ipv4IncludedRoutes, ipv6IncludedRoutes) + } + +#if os(iOS) /// Helper function allowing to parse comma-separated string of addresses. private func parseAddresses(fromString string: String) -> [IpAddrMask] { var addresses: [IpAddrMask] = [] - for addr in string.split(separator: ",").map({ String($0.trimmingCharacters(in: .whitespaces)) }) { + for addr in string.split(separator: ",").map({ + String($0.trimmingCharacters(in: .whitespaces)) + }) { if let addr_mask = IpAddrMask(fromString: addr) { addresses.append(addr_mask) } @@ -80,11 +147,11 @@ final class TunnelConfiguration: Codable { init(fromStartData startData: TunnelStartData) { name = startData.locationName - interface = InterfaceConfiguration(privateKey: startData.privateKey) + privateKey = startData.privateKey let peer = Peer(publicKey: startData.publicKey) peers = [peer] - interface.addresses = self.parseAddresses(fromString: startData.address) + addresses = self.parseAddresses(fromString: startData.address) // DNS settings let dnsRecords = startData.dns?.split(separator: ",").map { @@ -93,9 +160,9 @@ final class TunnelConfiguration: Codable { if !dnsRecords.isEmpty { for record in dnsRecords { if IPv4Address(record) != nil || IPv6Address(record) != nil { - interface.dns.append(record) + dns.append(record) } else { - interface.dnsSearch.append(record) + dnsSearch.append(record) } } } @@ -104,22 +171,21 @@ final class TunnelConfiguration: Codable { peer.preSharedKey = startData.presharedKey peer.endpoint = Endpoint(from: startData.endpoint) peer.persistentKeepAlive = UInt16(startData.keepalive) - peer.allowedIPs = switch startData.traffic { + peer.allowedIPs = + switch startData.traffic { case .All: [ IpAddrMask(address: IPv4Address.any, cidr: 0), - IpAddrMask(address: IPv6Address.any, cidr: 0) + IpAddrMask(address: IPv6Address.any, cidr: 0), ] case .Predefined: self.parseAddresses(fromString: startData.allowedIps) } } -} +#endif -//extension TunnelConfiguration: Equatable { -// public static func == (lhs: TunnelConfiguration, rhs: TunnelConfiguration) -> Bool { -// return lhs.name == rhs.name && -// lhs.interface == rhs.interface && -// Set(lhs.peers) == Set(rhs.peers) -// } -//} + /// Client connection expects one peer, so check for that. + func isValidForClientConnection() -> Bool { + return peers.count == 1 + } +} diff --git a/client/ios/VPNExtension/VPNExtension.entitlements b/client/ios/VPNExtension/VPNExtension.entitlements index 76663a0..8b31f34 100644 --- a/client/ios/VPNExtension/VPNExtension.entitlements +++ b/client/ios/VPNExtension/VPNExtension.entitlements @@ -10,6 +10,10 @@ allow-vpn + com.apple.security.application-groups + + group.net.defguard.mobile + com.apple.security.app-sandbox com.apple.security.network.client diff --git a/client/ios/VPNExtension/VPNExtensionDebug.entitlements b/client/ios/VPNExtension/VPNExtensionDebug.entitlements deleted file mode 100644 index e4dc971..0000000 --- a/client/ios/VPNExtension/VPNExtensionDebug.entitlements +++ /dev/null @@ -1,24 +0,0 @@ - - - - - com.apple.developer.networking.networkextension - - packet-tunnel-provider - - com.apple.developer.networking.vpn.api - - allow-vpn - - com.apple.security.app-sandbox - - com.apple.security.application-groups - - group.net.defguard.mobile - - com.apple.security.network.client - - com.apple.security.network.server - - - diff --git a/client/ios/boringtun b/client/ios/boringtun index f47e80a..bb10112 160000 --- a/client/ios/boringtun +++ b/client/ios/boringtun @@ -1 +1 @@ -Subproject commit f47e80a96923733bb9ed2bd5590f882dfb1d9b95 +Subproject commit bb1011289f31ad7544f6dfb1fcaac74e608f7911 diff --git a/client/lib/data/db/database.dart b/client/lib/data/db/database.dart index 5c34f33..eb9c7aa 100644 --- a/client/lib/data/db/database.dart +++ b/client/lib/data/db/database.dart @@ -1,6 +1,7 @@ import "package:drift/drift.dart"; import "package:drift_flutter/drift_flutter.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:mobile/data/db/database.steps.dart"; import "package:mobile/data/db/enums.dart"; import "package:path_provider/path_provider.dart"; import "package:riverpod_annotation/riverpod_annotation.dart"; @@ -28,8 +29,10 @@ class DefguardInstances extends Table with AutoIncrementingPrimaryKey { TextColumn get poolingToken => text()(); - @JsonKey('disable_all_traffic') - BoolColumn get disableAllTraffic => boolean()(); + @JsonKey('client_traffic_policy') + IntColumn get clientTrafficPolicy => integer() + .withDefault(const Constant(0)) + .map(const ClientTrafficPolicyConverter())(); @JsonKey('enterprise_enabled') BoolColumn get enterpriseEnabled => boolean()(); @@ -42,6 +45,9 @@ class DefguardInstances extends Table with AutoIncrementingPrimaryKey { // tells if the secure biometric storage exists for this instance BoolColumn get mfaKeysStored => boolean()(); + + // openid provider display name configured on the server side + TextColumn get openidDisplayName => text().nullable()(); } @DataClassName('Location') @@ -88,6 +94,8 @@ class Locations extends Table with AutoIncrementingPrimaryKey { @JsonKey('location_mfa_mode') IntColumn get locationMfaMode => integer().nullable().map(const LocationMfaModeConverter())(); + @JsonKey('posture_check_required') + BoolColumn get postureCheckRequired => boolean().nullable()(); } @DriftDatabase(tables: [DefguardInstances, Locations]) @@ -95,7 +103,7 @@ class AppDatabase extends _$AppDatabase { AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection()); @override - int get schemaVersion => 1; + int get schemaVersion => 4; @override MigrationStrategy get migration { @@ -103,6 +111,36 @@ class AppDatabase extends _$AppDatabase { beforeOpen: (details) async { await customStatement('PRAGMA foreign_keys = ON'); }, + onUpgrade: stepByStep( + from1To2: (m, schema) async { + // 1. Add the new column manually. + // This ensures Drift doesn't trigger a "Recreate Table" that might + // drop 'disable_all_traffic' before we are done with it. + await customStatement( + 'ALTER TABLE defguard_instances ADD COLUMN client_traffic_policy INTEGER NOT NULL DEFAULT 0', + ); + // 2. Update values derived from the old column + await customStatement(''' + UPDATE defguard_instances + SET client_traffic_policy = + CASE WHEN disable_all_traffic = 1 THEN 1 ELSE 0 END; + '''); + // 3. Drop old "disable_all_traffic" column + await m.dropColumn(defguardInstances, "disable_all_traffic"); + }, + from2To3: (m, schema) async { + await m.addColumn( + schema.defguardInstances, + schema.defguardInstances.openidDisplayName, + ); + }, + from3To4: (m, schema) async { + await m.addColumn( + schema.locations, + schema.locations.postureCheckRequired, + ); + }, + ), ); } diff --git a/client/lib/data/db/database.g.dart b/client/lib/data/db/database.g.dart index 2fca15c..1638e6f 100644 --- a/client/lib/data/db/database.g.dart +++ b/client/lib/data/db/database.g.dart @@ -93,20 +93,19 @@ class $DefguardInstancesTable extends DefguardInstances type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _disableAllTrafficMeta = const VerificationMeta( - 'disableAllTraffic', - ); @override - late final GeneratedColumn disableAllTraffic = GeneratedColumn( - 'disable_all_traffic', - aliasedName, - false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("disable_all_traffic" IN (0, 1))', - ), - ); + late final GeneratedColumnWithTypeConverter + clientTrafficPolicy = + GeneratedColumn( + 'client_traffic_policy', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ).withConverter( + $DefguardInstancesTable.$converterclientTrafficPolicy, + ); static const VerificationMeta _enterpriseEnabledMeta = const VerificationMeta( 'enterpriseEnabled', ); @@ -155,6 +154,18 @@ class $DefguardInstancesTable extends DefguardInstances 'CHECK ("mfa_keys_stored" IN (0, 1))', ), ); + static const VerificationMeta _openidDisplayNameMeta = const VerificationMeta( + 'openidDisplayName', + ); + @override + late final GeneratedColumn openidDisplayName = + GeneratedColumn( + 'openid_display_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ id, @@ -165,11 +176,12 @@ class $DefguardInstancesTable extends DefguardInstances proxyUrl, username, poolingToken, - disableAllTraffic, + clientTrafficPolicy, enterpriseEnabled, pubKey, privateKey, mfaKeysStored, + openidDisplayName, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -245,17 +257,6 @@ class $DefguardInstancesTable extends DefguardInstances } else if (isInserting) { context.missing(_poolingTokenMeta); } - if (data.containsKey('disable_all_traffic')) { - context.handle( - _disableAllTrafficMeta, - disableAllTraffic.isAcceptableOrUnknown( - data['disable_all_traffic']!, - _disableAllTrafficMeta, - ), - ); - } else if (isInserting) { - context.missing(_disableAllTrafficMeta); - } if (data.containsKey('enterprise_enabled')) { context.handle( _enterpriseEnabledMeta, @@ -294,6 +295,15 @@ class $DefguardInstancesTable extends DefguardInstances } else if (isInserting) { context.missing(_mfaKeysStoredMeta); } + if (data.containsKey('openid_display_name')) { + context.handle( + _openidDisplayNameMeta, + openidDisplayName.isAcceptableOrUnknown( + data['openid_display_name']!, + _openidDisplayNameMeta, + ), + ); + } return context; } @@ -335,10 +345,13 @@ class $DefguardInstancesTable extends DefguardInstances DriftSqlType.string, data['${effectivePrefix}pooling_token'], )!, - disableAllTraffic: attachedDatabase.typeMapping.read( - DriftSqlType.bool, - data['${effectivePrefix}disable_all_traffic'], - )!, + clientTrafficPolicy: $DefguardInstancesTable.$converterclientTrafficPolicy + .fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}client_traffic_policy'], + )!, + ), enterpriseEnabled: attachedDatabase.typeMapping.read( DriftSqlType.bool, data['${effectivePrefix}enterprise_enabled'], @@ -355,6 +368,10 @@ class $DefguardInstancesTable extends DefguardInstances DriftSqlType.bool, data['${effectivePrefix}mfa_keys_stored'], )!, + openidDisplayName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}openid_display_name'], + ), ); } @@ -362,6 +379,9 @@ class $DefguardInstancesTable extends DefguardInstances $DefguardInstancesTable createAlias(String alias) { return $DefguardInstancesTable(attachedDatabase, alias); } + + static TypeConverter $converterclientTrafficPolicy = + const ClientTrafficPolicyConverter(); } class DefguardInstance extends DataClass @@ -374,11 +394,12 @@ class DefguardInstance extends DataClass final String proxyUrl; final String username; final String poolingToken; - final bool disableAllTraffic; + final ClientTrafficPolicy clientTrafficPolicy; final bool enterpriseEnabled; final String pubKey; final String privateKey; final bool mfaKeysStored; + final String? openidDisplayName; const DefguardInstance({ required this.id, required this.name, @@ -388,11 +409,12 @@ class DefguardInstance extends DataClass required this.proxyUrl, required this.username, required this.poolingToken, - required this.disableAllTraffic, + required this.clientTrafficPolicy, required this.enterpriseEnabled, required this.pubKey, required this.privateKey, required this.mfaKeysStored, + this.openidDisplayName, }); @override Map toColumns(bool nullToAbsent) { @@ -405,11 +427,20 @@ class DefguardInstance extends DataClass map['proxy_url'] = Variable(proxyUrl); map['username'] = Variable(username); map['pooling_token'] = Variable(poolingToken); - map['disable_all_traffic'] = Variable(disableAllTraffic); + { + map['client_traffic_policy'] = Variable( + $DefguardInstancesTable.$converterclientTrafficPolicy.toSql( + clientTrafficPolicy, + ), + ); + } map['enterprise_enabled'] = Variable(enterpriseEnabled); map['pub_key'] = Variable(pubKey); map['private_key'] = Variable(privateKey); map['mfa_keys_stored'] = Variable(mfaKeysStored); + if (!nullToAbsent || openidDisplayName != null) { + map['openid_display_name'] = Variable(openidDisplayName); + } return map; } @@ -423,11 +454,14 @@ class DefguardInstance extends DataClass proxyUrl: Value(proxyUrl), username: Value(username), poolingToken: Value(poolingToken), - disableAllTraffic: Value(disableAllTraffic), + clientTrafficPolicy: Value(clientTrafficPolicy), enterpriseEnabled: Value(enterpriseEnabled), pubKey: Value(pubKey), privateKey: Value(privateKey), mfaKeysStored: Value(mfaKeysStored), + openidDisplayName: openidDisplayName == null && nullToAbsent + ? const Value.absent() + : Value(openidDisplayName), ); } @@ -445,11 +479,16 @@ class DefguardInstance extends DataClass proxyUrl: serializer.fromJson(json['proxy_url']), username: serializer.fromJson(json['username']), poolingToken: serializer.fromJson(json['poolingToken']), - disableAllTraffic: serializer.fromJson(json['disable_all_traffic']), + clientTrafficPolicy: serializer.fromJson( + json['client_traffic_policy'], + ), enterpriseEnabled: serializer.fromJson(json['enterprise_enabled']), pubKey: serializer.fromJson(json['pubKey']), privateKey: serializer.fromJson(json['privateKey']), mfaKeysStored: serializer.fromJson(json['mfaKeysStored']), + openidDisplayName: serializer.fromJson( + json['openidDisplayName'], + ), ); } @override @@ -464,11 +503,14 @@ class DefguardInstance extends DataClass 'proxy_url': serializer.toJson(proxyUrl), 'username': serializer.toJson(username), 'poolingToken': serializer.toJson(poolingToken), - 'disable_all_traffic': serializer.toJson(disableAllTraffic), + 'client_traffic_policy': serializer.toJson( + clientTrafficPolicy, + ), 'enterprise_enabled': serializer.toJson(enterpriseEnabled), 'pubKey': serializer.toJson(pubKey), 'privateKey': serializer.toJson(privateKey), 'mfaKeysStored': serializer.toJson(mfaKeysStored), + 'openidDisplayName': serializer.toJson(openidDisplayName), }; } @@ -481,11 +523,12 @@ class DefguardInstance extends DataClass String? proxyUrl, String? username, String? poolingToken, - bool? disableAllTraffic, + ClientTrafficPolicy? clientTrafficPolicy, bool? enterpriseEnabled, String? pubKey, String? privateKey, bool? mfaKeysStored, + Value openidDisplayName = const Value.absent(), }) => DefguardInstance( id: id ?? this.id, name: name ?? this.name, @@ -495,11 +538,14 @@ class DefguardInstance extends DataClass proxyUrl: proxyUrl ?? this.proxyUrl, username: username ?? this.username, poolingToken: poolingToken ?? this.poolingToken, - disableAllTraffic: disableAllTraffic ?? this.disableAllTraffic, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, pubKey: pubKey ?? this.pubKey, privateKey: privateKey ?? this.privateKey, mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + openidDisplayName: openidDisplayName.present + ? openidDisplayName.value + : this.openidDisplayName, ); DefguardInstance copyWithCompanion(DefguardInstancesCompanion data) { return DefguardInstance( @@ -513,9 +559,9 @@ class DefguardInstance extends DataClass poolingToken: data.poolingToken.present ? data.poolingToken.value : this.poolingToken, - disableAllTraffic: data.disableAllTraffic.present - ? data.disableAllTraffic.value - : this.disableAllTraffic, + clientTrafficPolicy: data.clientTrafficPolicy.present + ? data.clientTrafficPolicy.value + : this.clientTrafficPolicy, enterpriseEnabled: data.enterpriseEnabled.present ? data.enterpriseEnabled.value : this.enterpriseEnabled, @@ -526,6 +572,9 @@ class DefguardInstance extends DataClass mfaKeysStored: data.mfaKeysStored.present ? data.mfaKeysStored.value : this.mfaKeysStored, + openidDisplayName: data.openidDisplayName.present + ? data.openidDisplayName.value + : this.openidDisplayName, ); } @@ -540,11 +589,12 @@ class DefguardInstance extends DataClass ..write('proxyUrl: $proxyUrl, ') ..write('username: $username, ') ..write('poolingToken: $poolingToken, ') - ..write('disableAllTraffic: $disableAllTraffic, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') ..write('enterpriseEnabled: $enterpriseEnabled, ') ..write('pubKey: $pubKey, ') ..write('privateKey: $privateKey, ') - ..write('mfaKeysStored: $mfaKeysStored') + ..write('mfaKeysStored: $mfaKeysStored, ') + ..write('openidDisplayName: $openidDisplayName') ..write(')')) .toString(); } @@ -559,11 +609,12 @@ class DefguardInstance extends DataClass proxyUrl, username, poolingToken, - disableAllTraffic, + clientTrafficPolicy, enterpriseEnabled, pubKey, privateKey, mfaKeysStored, + openidDisplayName, ); @override bool operator ==(Object other) => @@ -577,11 +628,12 @@ class DefguardInstance extends DataClass other.proxyUrl == this.proxyUrl && other.username == this.username && other.poolingToken == this.poolingToken && - other.disableAllTraffic == this.disableAllTraffic && + other.clientTrafficPolicy == this.clientTrafficPolicy && other.enterpriseEnabled == this.enterpriseEnabled && other.pubKey == this.pubKey && other.privateKey == this.privateKey && - other.mfaKeysStored == this.mfaKeysStored); + other.mfaKeysStored == this.mfaKeysStored && + other.openidDisplayName == this.openidDisplayName); } class DefguardInstancesCompanion extends UpdateCompanion { @@ -593,11 +645,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { final Value proxyUrl; final Value username; final Value poolingToken; - final Value disableAllTraffic; + final Value clientTrafficPolicy; final Value enterpriseEnabled; final Value pubKey; final Value privateKey; final Value mfaKeysStored; + final Value openidDisplayName; const DefguardInstancesCompanion({ this.id = const Value.absent(), this.name = const Value.absent(), @@ -607,11 +660,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { this.proxyUrl = const Value.absent(), this.username = const Value.absent(), this.poolingToken = const Value.absent(), - this.disableAllTraffic = const Value.absent(), + this.clientTrafficPolicy = const Value.absent(), this.enterpriseEnabled = const Value.absent(), this.pubKey = const Value.absent(), this.privateKey = const Value.absent(), this.mfaKeysStored = const Value.absent(), + this.openidDisplayName = const Value.absent(), }); DefguardInstancesCompanion.insert({ this.id = const Value.absent(), @@ -622,11 +676,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { required String proxyUrl, required String username, required String poolingToken, - required bool disableAllTraffic, + this.clientTrafficPolicy = const Value.absent(), required bool enterpriseEnabled, required String pubKey, required String privateKey, required bool mfaKeysStored, + this.openidDisplayName = const Value.absent(), }) : name = Value(name), uuid = Value(uuid), url = Value(url), @@ -634,7 +689,6 @@ class DefguardInstancesCompanion extends UpdateCompanion { proxyUrl = Value(proxyUrl), username = Value(username), poolingToken = Value(poolingToken), - disableAllTraffic = Value(disableAllTraffic), enterpriseEnabled = Value(enterpriseEnabled), pubKey = Value(pubKey), privateKey = Value(privateKey), @@ -648,11 +702,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { Expression? proxyUrl, Expression? username, Expression? poolingToken, - Expression? disableAllTraffic, + Expression? clientTrafficPolicy, Expression? enterpriseEnabled, Expression? pubKey, Expression? privateKey, Expression? mfaKeysStored, + Expression? openidDisplayName, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -663,11 +718,13 @@ class DefguardInstancesCompanion extends UpdateCompanion { if (proxyUrl != null) 'proxy_url': proxyUrl, if (username != null) 'username': username, if (poolingToken != null) 'pooling_token': poolingToken, - if (disableAllTraffic != null) 'disable_all_traffic': disableAllTraffic, + if (clientTrafficPolicy != null) + 'client_traffic_policy': clientTrafficPolicy, if (enterpriseEnabled != null) 'enterprise_enabled': enterpriseEnabled, if (pubKey != null) 'pub_key': pubKey, if (privateKey != null) 'private_key': privateKey, if (mfaKeysStored != null) 'mfa_keys_stored': mfaKeysStored, + if (openidDisplayName != null) 'openid_display_name': openidDisplayName, }); } @@ -680,11 +737,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { Value? proxyUrl, Value? username, Value? poolingToken, - Value? disableAllTraffic, + Value? clientTrafficPolicy, Value? enterpriseEnabled, Value? pubKey, Value? privateKey, Value? mfaKeysStored, + Value? openidDisplayName, }) { return DefguardInstancesCompanion( id: id ?? this.id, @@ -695,11 +753,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { proxyUrl: proxyUrl ?? this.proxyUrl, username: username ?? this.username, poolingToken: poolingToken ?? this.poolingToken, - disableAllTraffic: disableAllTraffic ?? this.disableAllTraffic, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, pubKey: pubKey ?? this.pubKey, privateKey: privateKey ?? this.privateKey, mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + openidDisplayName: openidDisplayName ?? this.openidDisplayName, ); } @@ -730,8 +789,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { if (poolingToken.present) { map['pooling_token'] = Variable(poolingToken.value); } - if (disableAllTraffic.present) { - map['disable_all_traffic'] = Variable(disableAllTraffic.value); + if (clientTrafficPolicy.present) { + map['client_traffic_policy'] = Variable( + $DefguardInstancesTable.$converterclientTrafficPolicy.toSql( + clientTrafficPolicy.value, + ), + ); } if (enterpriseEnabled.present) { map['enterprise_enabled'] = Variable(enterpriseEnabled.value); @@ -745,6 +808,9 @@ class DefguardInstancesCompanion extends UpdateCompanion { if (mfaKeysStored.present) { map['mfa_keys_stored'] = Variable(mfaKeysStored.value); } + if (openidDisplayName.present) { + map['openid_display_name'] = Variable(openidDisplayName.value); + } return map; } @@ -759,11 +825,12 @@ class DefguardInstancesCompanion extends UpdateCompanion { ..write('proxyUrl: $proxyUrl, ') ..write('username: $username, ') ..write('poolingToken: $poolingToken, ') - ..write('disableAllTraffic: $disableAllTraffic, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') ..write('enterpriseEnabled: $enterpriseEnabled, ') ..write('pubKey: $pubKey, ') ..write('privateKey: $privateKey, ') - ..write('mfaKeysStored: $mfaKeysStored') + ..write('mfaKeysStored: $mfaKeysStored, ') + ..write('openidDisplayName: $openidDisplayName') ..write(')')) .toString(); } @@ -925,6 +992,19 @@ class $LocationsTable extends Locations type: DriftSqlType.int, requiredDuringInsert: false, ).withConverter($LocationsTable.$converterlocationMfaModen); + static const VerificationMeta _postureCheckRequiredMeta = + const VerificationMeta('postureCheckRequired'); + @override + late final GeneratedColumn postureCheckRequired = GeneratedColumn( + 'posture_check_required', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("posture_check_required" IN (0, 1))', + ), + ); @override List get $columns => [ id, @@ -941,6 +1021,7 @@ class $LocationsTable extends Locations mfaMethod, keepAliveInterval, locationMfaMode, + postureCheckRequired, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -1036,6 +1117,15 @@ class $LocationsTable extends Locations } else if (isInserting) { context.missing(_keepAliveIntervalMeta); } + if (data.containsKey('posture_check_required')) { + context.handle( + _postureCheckRequiredMeta, + postureCheckRequired.isAcceptableOrUnknown( + data['posture_check_required']!, + _postureCheckRequiredMeta, + ), + ); + } return context; } @@ -1107,6 +1197,10 @@ class $LocationsTable extends Locations data['${effectivePrefix}location_mfa_mode'], ), ), + postureCheckRequired: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}posture_check_required'], + ), ); } @@ -1148,6 +1242,7 @@ class Location extends DataClass implements Insertable { final MfaMethod? mfaMethod; final int keepAliveInterval; final LocationMfaMode? locationMfaMode; + final bool? postureCheckRequired; const Location({ required this.id, required this.instance, @@ -1163,6 +1258,7 @@ class Location extends DataClass implements Insertable { this.mfaMethod, required this.keepAliveInterval, this.locationMfaMode, + this.postureCheckRequired, }); @override Map toColumns(bool nullToAbsent) { @@ -1197,6 +1293,9 @@ class Location extends DataClass implements Insertable { $LocationsTable.$converterlocationMfaModen.toSql(locationMfaMode), ); } + if (!nullToAbsent || postureCheckRequired != null) { + map['posture_check_required'] = Variable(postureCheckRequired); + } return map; } @@ -1224,6 +1323,9 @@ class Location extends DataClass implements Insertable { locationMfaMode: locationMfaMode == null && nullToAbsent ? const Value.absent() : Value(locationMfaMode), + postureCheckRequired: postureCheckRequired == null && nullToAbsent + ? const Value.absent() + : Value(postureCheckRequired), ); } @@ -1251,6 +1353,9 @@ class Location extends DataClass implements Insertable { locationMfaMode: serializer.fromJson( json['location_mfa_mode'], ), + postureCheckRequired: serializer.fromJson( + json['posture_check_required'], + ), ); } @override @@ -1273,6 +1378,7 @@ class Location extends DataClass implements Insertable { 'mfa_method': serializer.toJson(mfaMethod), 'keepalive_interval': serializer.toJson(keepAliveInterval), 'location_mfa_mode': serializer.toJson(locationMfaMode), + 'posture_check_required': serializer.toJson(postureCheckRequired), }; } @@ -1291,6 +1397,7 @@ class Location extends DataClass implements Insertable { Value mfaMethod = const Value.absent(), int? keepAliveInterval, Value locationMfaMode = const Value.absent(), + Value postureCheckRequired = const Value.absent(), }) => Location( id: id ?? this.id, instance: instance ?? this.instance, @@ -1310,6 +1417,9 @@ class Location extends DataClass implements Insertable { locationMfaMode: locationMfaMode.present ? locationMfaMode.value : this.locationMfaMode, + postureCheckRequired: postureCheckRequired.present + ? postureCheckRequired.value + : this.postureCheckRequired, ); Location copyWithCompanion(LocationsCompanion data) { return Location( @@ -1337,6 +1447,9 @@ class Location extends DataClass implements Insertable { locationMfaMode: data.locationMfaMode.present ? data.locationMfaMode.value : this.locationMfaMode, + postureCheckRequired: data.postureCheckRequired.present + ? data.postureCheckRequired.value + : this.postureCheckRequired, ); } @@ -1356,7 +1469,8 @@ class Location extends DataClass implements Insertable { ..write('trafficMethod: $trafficMethod, ') ..write('mfaMethod: $mfaMethod, ') ..write('keepAliveInterval: $keepAliveInterval, ') - ..write('locationMfaMode: $locationMfaMode') + ..write('locationMfaMode: $locationMfaMode, ') + ..write('postureCheckRequired: $postureCheckRequired') ..write(')')) .toString(); } @@ -1377,6 +1491,7 @@ class Location extends DataClass implements Insertable { mfaMethod, keepAliveInterval, locationMfaMode, + postureCheckRequired, ); @override bool operator ==(Object other) => @@ -1395,7 +1510,8 @@ class Location extends DataClass implements Insertable { other.trafficMethod == this.trafficMethod && other.mfaMethod == this.mfaMethod && other.keepAliveInterval == this.keepAliveInterval && - other.locationMfaMode == this.locationMfaMode); + other.locationMfaMode == this.locationMfaMode && + other.postureCheckRequired == this.postureCheckRequired); } class LocationsCompanion extends UpdateCompanion { @@ -1413,6 +1529,7 @@ class LocationsCompanion extends UpdateCompanion { final Value mfaMethod; final Value keepAliveInterval; final Value locationMfaMode; + final Value postureCheckRequired; const LocationsCompanion({ this.id = const Value.absent(), this.instance = const Value.absent(), @@ -1428,6 +1545,7 @@ class LocationsCompanion extends UpdateCompanion { this.mfaMethod = const Value.absent(), this.keepAliveInterval = const Value.absent(), this.locationMfaMode = const Value.absent(), + this.postureCheckRequired = const Value.absent(), }); LocationsCompanion.insert({ this.id = const Value.absent(), @@ -1444,6 +1562,7 @@ class LocationsCompanion extends UpdateCompanion { this.mfaMethod = const Value.absent(), required int keepAliveInterval, this.locationMfaMode = const Value.absent(), + this.postureCheckRequired = const Value.absent(), }) : instance = Value(instance), networkId = Value(networkId), name = Value(name), @@ -1467,6 +1586,7 @@ class LocationsCompanion extends UpdateCompanion { Expression? mfaMethod, Expression? keepAliveInterval, Expression? locationMfaMode, + Expression? postureCheckRequired, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -1483,6 +1603,8 @@ class LocationsCompanion extends UpdateCompanion { if (mfaMethod != null) 'mfa_method': mfaMethod, if (keepAliveInterval != null) 'keep_alive_interval': keepAliveInterval, if (locationMfaMode != null) 'location_mfa_mode': locationMfaMode, + if (postureCheckRequired != null) + 'posture_check_required': postureCheckRequired, }); } @@ -1501,6 +1623,7 @@ class LocationsCompanion extends UpdateCompanion { Value? mfaMethod, Value? keepAliveInterval, Value? locationMfaMode, + Value? postureCheckRequired, }) { return LocationsCompanion( id: id ?? this.id, @@ -1517,6 +1640,7 @@ class LocationsCompanion extends UpdateCompanion { mfaMethod: mfaMethod ?? this.mfaMethod, keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, locationMfaMode: locationMfaMode ?? this.locationMfaMode, + postureCheckRequired: postureCheckRequired ?? this.postureCheckRequired, ); } @@ -1571,6 +1695,11 @@ class LocationsCompanion extends UpdateCompanion { $LocationsTable.$converterlocationMfaModen.toSql(locationMfaMode.value), ); } + if (postureCheckRequired.present) { + map['posture_check_required'] = Variable( + postureCheckRequired.value, + ); + } return map; } @@ -1590,7 +1719,8 @@ class LocationsCompanion extends UpdateCompanion { ..write('trafficMethod: $trafficMethod, ') ..write('mfaMethod: $mfaMethod, ') ..write('keepAliveInterval: $keepAliveInterval, ') - ..write('locationMfaMode: $locationMfaMode') + ..write('locationMfaMode: $locationMfaMode, ') + ..write('postureCheckRequired: $postureCheckRequired') ..write(')')) .toString(); } @@ -1632,11 +1762,12 @@ typedef $$DefguardInstancesTableCreateCompanionBuilder = required String proxyUrl, required String username, required String poolingToken, - required bool disableAllTraffic, + Value clientTrafficPolicy, required bool enterpriseEnabled, required String pubKey, required String privateKey, required bool mfaKeysStored, + Value openidDisplayName, }); typedef $$DefguardInstancesTableUpdateCompanionBuilder = DefguardInstancesCompanion Function({ @@ -1648,11 +1779,12 @@ typedef $$DefguardInstancesTableUpdateCompanionBuilder = Value proxyUrl, Value username, Value poolingToken, - Value disableAllTraffic, + Value clientTrafficPolicy, Value enterpriseEnabled, Value pubKey, Value privateKey, Value mfaKeysStored, + Value openidDisplayName, }); final class $$DefguardInstancesTableReferences @@ -1739,9 +1871,10 @@ class $$DefguardInstancesTableFilterComposer builder: (column) => ColumnFilters(column), ); - ColumnFilters get disableAllTraffic => $composableBuilder( - column: $table.disableAllTraffic, - builder: (column) => ColumnFilters(column), + ColumnWithTypeConverterFilters + get clientTrafficPolicy => $composableBuilder( + column: $table.clientTrafficPolicy, + builder: (column) => ColumnWithTypeConverterFilters(column), ); ColumnFilters get enterpriseEnabled => $composableBuilder( @@ -1764,6 +1897,11 @@ class $$DefguardInstancesTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get openidDisplayName => $composableBuilder( + column: $table.openidDisplayName, + builder: (column) => ColumnFilters(column), + ); + Expression locationsRefs( Expression Function($$LocationsTableFilterComposer f) f, ) { @@ -1839,8 +1977,8 @@ class $$DefguardInstancesTableOrderingComposer builder: (column) => ColumnOrderings(column), ); - ColumnOrderings get disableAllTraffic => $composableBuilder( - column: $table.disableAllTraffic, + ColumnOrderings get clientTrafficPolicy => $composableBuilder( + column: $table.clientTrafficPolicy, builder: (column) => ColumnOrderings(column), ); @@ -1863,6 +2001,11 @@ class $$DefguardInstancesTableOrderingComposer column: $table.mfaKeysStored, builder: (column) => ColumnOrderings(column), ); + + ColumnOrderings get openidDisplayName => $composableBuilder( + column: $table.openidDisplayName, + builder: (column) => ColumnOrderings(column), + ); } class $$DefguardInstancesTableAnnotationComposer @@ -1900,8 +2043,9 @@ class $$DefguardInstancesTableAnnotationComposer builder: (column) => column, ); - GeneratedColumn get disableAllTraffic => $composableBuilder( - column: $table.disableAllTraffic, + GeneratedColumnWithTypeConverter + get clientTrafficPolicy => $composableBuilder( + column: $table.clientTrafficPolicy, builder: (column) => column, ); @@ -1923,6 +2067,11 @@ class $$DefguardInstancesTableAnnotationComposer builder: (column) => column, ); + GeneratedColumn get openidDisplayName => $composableBuilder( + column: $table.openidDisplayName, + builder: (column) => column, + ); + Expression locationsRefs( Expression Function($$LocationsTableAnnotationComposer a) f, ) { @@ -1990,11 +2139,13 @@ class $$DefguardInstancesTableTableManager Value proxyUrl = const Value.absent(), Value username = const Value.absent(), Value poolingToken = const Value.absent(), - Value disableAllTraffic = const Value.absent(), + Value clientTrafficPolicy = + const Value.absent(), Value enterpriseEnabled = const Value.absent(), Value pubKey = const Value.absent(), Value privateKey = const Value.absent(), Value mfaKeysStored = const Value.absent(), + Value openidDisplayName = const Value.absent(), }) => DefguardInstancesCompanion( id: id, name: name, @@ -2004,11 +2155,12 @@ class $$DefguardInstancesTableTableManager proxyUrl: proxyUrl, username: username, poolingToken: poolingToken, - disableAllTraffic: disableAllTraffic, + clientTrafficPolicy: clientTrafficPolicy, enterpriseEnabled: enterpriseEnabled, pubKey: pubKey, privateKey: privateKey, mfaKeysStored: mfaKeysStored, + openidDisplayName: openidDisplayName, ), createCompanionCallback: ({ @@ -2020,11 +2172,13 @@ class $$DefguardInstancesTableTableManager required String proxyUrl, required String username, required String poolingToken, - required bool disableAllTraffic, + Value clientTrafficPolicy = + const Value.absent(), required bool enterpriseEnabled, required String pubKey, required String privateKey, required bool mfaKeysStored, + Value openidDisplayName = const Value.absent(), }) => DefguardInstancesCompanion.insert( id: id, name: name, @@ -2034,11 +2188,12 @@ class $$DefguardInstancesTableTableManager proxyUrl: proxyUrl, username: username, poolingToken: poolingToken, - disableAllTraffic: disableAllTraffic, + clientTrafficPolicy: clientTrafficPolicy, enterpriseEnabled: enterpriseEnabled, pubKey: pubKey, privateKey: privateKey, mfaKeysStored: mfaKeysStored, + openidDisplayName: openidDisplayName, ), withReferenceMapper: (p0) => p0 .map( @@ -2112,6 +2267,7 @@ typedef $$LocationsTableCreateCompanionBuilder = Value mfaMethod, required int keepAliveInterval, Value locationMfaMode, + Value postureCheckRequired, }); typedef $$LocationsTableUpdateCompanionBuilder = LocationsCompanion Function({ @@ -2129,6 +2285,7 @@ typedef $$LocationsTableUpdateCompanionBuilder = Value mfaMethod, Value keepAliveInterval, Value locationMfaMode, + Value postureCheckRequired, }); final class $$LocationsTableReferences @@ -2232,6 +2389,11 @@ class $$LocationsTableFilterComposer builder: (column) => ColumnWithTypeConverterFilters(column), ); + ColumnFilters get postureCheckRequired => $composableBuilder( + column: $table.postureCheckRequired, + builder: (column) => ColumnFilters(column), + ); + $$DefguardInstancesTableFilterComposer get instance { final $$DefguardInstancesTableFilterComposer composer = $composerBuilder( composer: this, @@ -2330,6 +2492,11 @@ class $$LocationsTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get postureCheckRequired => $composableBuilder( + column: $table.postureCheckRequired, + builder: (column) => ColumnOrderings(column), + ); + $$DefguardInstancesTableOrderingComposer get instance { final $$DefguardInstancesTableOrderingComposer composer = $composerBuilder( composer: this, @@ -2414,6 +2581,11 @@ class $$LocationsTableAnnotationComposer builder: (column) => column, ); + GeneratedColumn get postureCheckRequired => $composableBuilder( + column: $table.postureCheckRequired, + builder: (column) => column, + ); + $$DefguardInstancesTableAnnotationComposer get instance { final $$DefguardInstancesTableAnnotationComposer composer = $composerBuilder( @@ -2481,6 +2653,7 @@ class $$LocationsTableTableManager Value mfaMethod = const Value.absent(), Value keepAliveInterval = const Value.absent(), Value locationMfaMode = const Value.absent(), + Value postureCheckRequired = const Value.absent(), }) => LocationsCompanion( id: id, instance: instance, @@ -2496,6 +2669,7 @@ class $$LocationsTableTableManager mfaMethod: mfaMethod, keepAliveInterval: keepAliveInterval, locationMfaMode: locationMfaMode, + postureCheckRequired: postureCheckRequired, ), createCompanionCallback: ({ @@ -2513,6 +2687,7 @@ class $$LocationsTableTableManager Value mfaMethod = const Value.absent(), required int keepAliveInterval, Value locationMfaMode = const Value.absent(), + Value postureCheckRequired = const Value.absent(), }) => LocationsCompanion.insert( id: id, instance: instance, @@ -2528,6 +2703,7 @@ class $$LocationsTableTableManager mfaMethod: mfaMethod, keepAliveInterval: keepAliveInterval, locationMfaMode: locationMfaMode, + postureCheckRequired: postureCheckRequired, ), withReferenceMapper: (p0) => p0 .map( diff --git a/client/lib/data/db/database.steps.dart b/client/lib/data/db/database.steps.dart new file mode 100644 index 0000000..8f97ee0 --- /dev/null +++ b/client/lib/data/db/database.steps.dart @@ -0,0 +1,561 @@ +// dart format width=80 +import 'package:drift/internal/versioned_schema.dart' as i0; +import 'package:drift/drift.dart' as i1; +import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import + +// GENERATED BY drift_dev, DO NOT MODIFY. +final class Schema2 extends i0.VersionedSchema { + Schema2({required super.database}) : super(version: 2); + @override + late final List entities = [ + defguardInstances, + locations, + ]; + late final Shape0 defguardInstances = Shape0( + source: i0.VersionedTable( + entityName: 'defguard_instances', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + _column_8, + _column_9, + _column_10, + _column_11, + _column_12, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 locations = Shape1( + source: i0.VersionedTable( + entityName: 'locations', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_13, + _column_14, + _column_1, + _column_15, + _column_10, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_22, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape0 extends i0.VersionedTable { + Shape0({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get uuid => + columnsByName['uuid']! as i1.GeneratedColumn; + i1.GeneratedColumn get url => + columnsByName['url']! as i1.GeneratedColumn; + i1.GeneratedColumn get deviceId => + columnsByName['device_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get proxyUrl => + columnsByName['proxy_url']! as i1.GeneratedColumn; + i1.GeneratedColumn get username => + columnsByName['username']! as i1.GeneratedColumn; + i1.GeneratedColumn get poolingToken => + columnsByName['pooling_token']! as i1.GeneratedColumn; + i1.GeneratedColumn get clientTrafficPolicy => + columnsByName['client_traffic_policy']! as i1.GeneratedColumn; + i1.GeneratedColumn get enterpriseEnabled => + columnsByName['enterprise_enabled']! as i1.GeneratedColumn; + i1.GeneratedColumn get pubKey => + columnsByName['pub_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get privateKey => + columnsByName['private_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get mfaKeysStored => + columnsByName['mfa_keys_stored']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_0(String aliasedName) => + i1.GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: i1.DriftSqlType.int, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); +i1.GeneratedColumn _column_1(String aliasedName) => + i1.GeneratedColumn( + 'name', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_2(String aliasedName) => + i1.GeneratedColumn( + 'uuid', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_3(String aliasedName) => + i1.GeneratedColumn( + 'url', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_4(String aliasedName) => + i1.GeneratedColumn( + 'device_id', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_5(String aliasedName) => + i1.GeneratedColumn( + 'proxy_url', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_6(String aliasedName) => + i1.GeneratedColumn( + 'username', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_7(String aliasedName) => + i1.GeneratedColumn( + 'pooling_token', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_8(String aliasedName) => + i1.GeneratedColumn( + 'client_traffic_policy', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_9(String aliasedName) => + i1.GeneratedColumn( + 'enterprise_enabled', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("enterprise_enabled" IN (0, 1))', + ), + ); +i1.GeneratedColumn _column_10(String aliasedName) => + i1.GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_11(String aliasedName) => + i1.GeneratedColumn( + 'private_key', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_12(String aliasedName) => + i1.GeneratedColumn( + 'mfa_keys_stored', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_keys_stored" IN (0, 1))', + ), + ); + +class Shape1 extends i0.VersionedTable { + Shape1({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get instance => + columnsByName['instance']! as i1.GeneratedColumn; + i1.GeneratedColumn get networkId => + columnsByName['network_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get address => + columnsByName['address']! as i1.GeneratedColumn; + i1.GeneratedColumn get pubKey => + columnsByName['pub_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get endpoint => + columnsByName['endpoint']! as i1.GeneratedColumn; + i1.GeneratedColumn get allowedIps => + columnsByName['allowed_ips']! as i1.GeneratedColumn; + i1.GeneratedColumn get dns => + columnsByName['dns']! as i1.GeneratedColumn; + i1.GeneratedColumn get mfaEnabled => + columnsByName['mfa_enabled']! as i1.GeneratedColumn; + i1.GeneratedColumn get trafficMethod => + columnsByName['traffic_method']! as i1.GeneratedColumn; + i1.GeneratedColumn get mfaMethod => + columnsByName['mfa_method']! as i1.GeneratedColumn; + i1.GeneratedColumn get keepAliveInterval => + columnsByName['keep_alive_interval']! as i1.GeneratedColumn; + i1.GeneratedColumn get locationMfaMode => + columnsByName['location_mfa_mode']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_13(String aliasedName) => + i1.GeneratedColumn( + 'instance', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES defguard_instances (id) ON DELETE CASCADE', + ), + ); +i1.GeneratedColumn _column_14(String aliasedName) => + i1.GeneratedColumn( + 'network_id', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_15(String aliasedName) => + i1.GeneratedColumn( + 'address', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_16(String aliasedName) => + i1.GeneratedColumn( + 'endpoint', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_17(String aliasedName) => + i1.GeneratedColumn( + 'allowed_ips', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_18(String aliasedName) => + i1.GeneratedColumn( + 'dns', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_19(String aliasedName) => + i1.GeneratedColumn( + 'mfa_enabled', + aliasedName, + true, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_enabled" IN (0, 1))', + ), + ); +i1.GeneratedColumn _column_20(String aliasedName) => + i1.GeneratedColumn( + 'traffic_method', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_21(String aliasedName) => + i1.GeneratedColumn( + 'mfa_method', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_22(String aliasedName) => + i1.GeneratedColumn( + 'keep_alive_interval', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_23(String aliasedName) => + i1.GeneratedColumn( + 'location_mfa_mode', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); + +final class Schema3 extends i0.VersionedSchema { + Schema3({required super.database}) : super(version: 3); + @override + late final List entities = [ + defguardInstances, + locations, + ]; + late final Shape2 defguardInstances = Shape2( + source: i0.VersionedTable( + entityName: 'defguard_instances', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + _column_8, + _column_9, + _column_10, + _column_11, + _column_12, + _column_24, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 locations = Shape1( + source: i0.VersionedTable( + entityName: 'locations', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_13, + _column_14, + _column_1, + _column_15, + _column_10, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_22, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape2 extends i0.VersionedTable { + Shape2({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get uuid => + columnsByName['uuid']! as i1.GeneratedColumn; + i1.GeneratedColumn get url => + columnsByName['url']! as i1.GeneratedColumn; + i1.GeneratedColumn get deviceId => + columnsByName['device_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get proxyUrl => + columnsByName['proxy_url']! as i1.GeneratedColumn; + i1.GeneratedColumn get username => + columnsByName['username']! as i1.GeneratedColumn; + i1.GeneratedColumn get poolingToken => + columnsByName['pooling_token']! as i1.GeneratedColumn; + i1.GeneratedColumn get clientTrafficPolicy => + columnsByName['client_traffic_policy']! as i1.GeneratedColumn; + i1.GeneratedColumn get enterpriseEnabled => + columnsByName['enterprise_enabled']! as i1.GeneratedColumn; + i1.GeneratedColumn get pubKey => + columnsByName['pub_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get privateKey => + columnsByName['private_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get mfaKeysStored => + columnsByName['mfa_keys_stored']! as i1.GeneratedColumn; + i1.GeneratedColumn get openidDisplayName => + columnsByName['openid_display_name']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_24(String aliasedName) => + i1.GeneratedColumn( + 'openid_display_name', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); + +final class Schema4 extends i0.VersionedSchema { + Schema4({required super.database}) : super(version: 4); + @override + late final List entities = [ + defguardInstances, + locations, + ]; + late final Shape2 defguardInstances = Shape2( + source: i0.VersionedTable( + entityName: 'defguard_instances', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + _column_8, + _column_9, + _column_10, + _column_11, + _column_12, + _column_24, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 locations = Shape3( + source: i0.VersionedTable( + entityName: 'locations', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_13, + _column_14, + _column_1, + _column_15, + _column_10, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_22, + _column_23, + _column_25, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape3 extends i0.VersionedTable { + Shape3({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get instance => + columnsByName['instance']! as i1.GeneratedColumn; + i1.GeneratedColumn get networkId => + columnsByName['network_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get address => + columnsByName['address']! as i1.GeneratedColumn; + i1.GeneratedColumn get pubKey => + columnsByName['pub_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get endpoint => + columnsByName['endpoint']! as i1.GeneratedColumn; + i1.GeneratedColumn get allowedIps => + columnsByName['allowed_ips']! as i1.GeneratedColumn; + i1.GeneratedColumn get dns => + columnsByName['dns']! as i1.GeneratedColumn; + i1.GeneratedColumn get mfaEnabled => + columnsByName['mfa_enabled']! as i1.GeneratedColumn; + i1.GeneratedColumn get trafficMethod => + columnsByName['traffic_method']! as i1.GeneratedColumn; + i1.GeneratedColumn get mfaMethod => + columnsByName['mfa_method']! as i1.GeneratedColumn; + i1.GeneratedColumn get keepAliveInterval => + columnsByName['keep_alive_interval']! as i1.GeneratedColumn; + i1.GeneratedColumn get locationMfaMode => + columnsByName['location_mfa_mode']! as i1.GeneratedColumn; + i1.GeneratedColumn get postureCheckRequired => + columnsByName['posture_check_required']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_25(String aliasedName) => + i1.GeneratedColumn( + 'posture_check_required', + aliasedName, + true, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("posture_check_required" IN (0, 1))', + ), + ); +i0.MigrationStepWithVersion migrationSteps({ + required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, +}) { + return (currentVersion, database) async { + switch (currentVersion) { + case 1: + final schema = Schema2(database: database); + final migrator = i1.Migrator(database, schema); + await from1To2(migrator, schema); + return 2; + case 2: + final schema = Schema3(database: database); + final migrator = i1.Migrator(database, schema); + await from2To3(migrator, schema); + return 3; + case 3: + final schema = Schema4(database: database); + final migrator = i1.Migrator(database, schema); + await from3To4(migrator, schema); + return 4; + default: + throw ArgumentError.value('Unknown migration from $currentVersion'); + } + }; +} + +i1.OnUpgrade stepByStep({ + required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, +}) => i0.VersionedSchema.stepByStepHelper( + step: migrationSteps( + from1To2: from1To2, + from2To3: from2To3, + from3To4: from3To4, + ), +); diff --git a/client/lib/data/db/enums.dart b/client/lib/data/db/enums.dart index 021b076..85c264e 100644 --- a/client/lib/data/db/enums.dart +++ b/client/lib/data/db/enums.dart @@ -83,3 +83,35 @@ class LocationMfaModeConverter extends TypeConverter { return value.value; } } + +@j.JsonEnum() +enum ClientTrafficPolicy { + @j.JsonValue(0) + none(0), + @j.JsonValue(1) + disableAllTraffic(1), + @j.JsonValue(2) + forceAllTraffic(2); + + final int value; + + const ClientTrafficPolicy(this.value); + + static ClientTrafficPolicy fromValue(int value) => + ClientTrafficPolicy.values.firstWhere((e) => e.value == value); +} + +class ClientTrafficPolicyConverter + extends TypeConverter { + const ClientTrafficPolicyConverter(); + + @override + ClientTrafficPolicy fromSql(int fromDb) { + return ClientTrafficPolicy.fromValue(fromDb); + } + + @override + int toSql(ClientTrafficPolicy value) { + return value.value; + } +} diff --git a/client/lib/data/plugin/plugin.dart b/client/lib/data/plugin/plugin.dart index 5bbc54c..4b7b7a9 100644 --- a/client/lib/data/plugin/plugin.dart +++ b/client/lib/data/plugin/plugin.dart @@ -4,8 +4,6 @@ import '../db/enums.dart'; part 'plugin.g.dart'; - - @JsonSerializable() class PluginConnectPayload { // config @@ -25,6 +23,7 @@ class PluginConnectPayload { final int instanceId; final int networkId; RoutingMethod traffic; + final bool postureCheckRequired; PluginConnectPayload({ required this.publicKey, @@ -41,6 +40,7 @@ class PluginConnectPayload { required this.networkId, this.dns, this.presharedKey, + required this.postureCheckRequired, }); factory PluginConnectPayload.fromJson(Map json) => @@ -49,7 +49,6 @@ class PluginConnectPayload { Map toJson() => _$PluginConnectPayloadToJson(this); } - @JsonSerializable() class PluginTunnelEventData { final int instanceId; @@ -62,7 +61,8 @@ class PluginTunnelEventData { required this.traffic, }); - factory PluginTunnelEventData.fromJson(Map json) => _$PluginTunnelEventDataFromJson(json); + factory PluginTunnelEventData.fromJson(Map json) => + _$PluginTunnelEventDataFromJson(json); Map toJson() => _$PluginTunnelEventDataToJson(this); } diff --git a/client/lib/data/plugin/plugin.g.dart b/client/lib/data/plugin/plugin.g.dart index 1b85e6a..6fb711c 100644 --- a/client/lib/data/plugin/plugin.g.dart +++ b/client/lib/data/plugin/plugin.g.dart @@ -30,6 +30,10 @@ PluginConnectPayload _$PluginConnectPayloadFromJson( networkId: $checkedConvert('network_id', (v) => (v as num).toInt()), dns: $checkedConvert('dns', (v) => v as String?), presharedKey: $checkedConvert('preshared_key', (v) => v as String?), + postureCheckRequired: $checkedConvert( + 'posture_check_required', + (v) => v as bool, + ), ); return val; }, @@ -43,6 +47,7 @@ PluginConnectPayload _$PluginConnectPayloadFromJson( 'instanceId': 'instance_id', 'networkId': 'network_id', 'presharedKey': 'preshared_key', + 'postureCheckRequired': 'posture_check_required', }, ); @@ -61,6 +66,7 @@ const _$PluginConnectPayloadFieldMap = { 'instanceId': 'instance_id', 'networkId': 'network_id', 'traffic': 'traffic', + 'postureCheckRequired': 'posture_check_required', }; Map _$PluginConnectPayloadToJson( @@ -80,6 +86,7 @@ Map _$PluginConnectPayloadToJson( 'instance_id': instance.instanceId, 'network_id': instance.networkId, 'traffic': _$RoutingMethodEnumMap[instance.traffic]!, + 'posture_check_required': instance.postureCheckRequired, }; const _$RoutingMethodEnumMap = { diff --git a/client/lib/data/proto/client_platform_info.pb.dart b/client/lib/data/proto/client_platform_info.pb.dart new file mode 100644 index 0000000..bdfab62 --- /dev/null +++ b/client/lib/data/proto/client_platform_info.pb.dart @@ -0,0 +1,148 @@ +// This is a generated file - do not edit. +// +// Generated from client_platform_info.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + +class ClientPlatformInfo extends $pb.GeneratedMessage { + factory ClientPlatformInfo({ + $core.String? osFamily, + $core.String? osType, + $core.String? version, + $core.String? edition, + $core.String? codename, + $core.String? bitness, + $core.String? architecture, + }) { + final result = create(); + if (osFamily != null) result.osFamily = osFamily; + if (osType != null) result.osType = osType; + if (version != null) result.version = version; + if (edition != null) result.edition = edition; + if (codename != null) result.codename = codename; + if (bitness != null) result.bitness = bitness; + if (architecture != null) result.architecture = architecture; + return result; + } + + ClientPlatformInfo._(); + + factory ClientPlatformInfo.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory ClientPlatformInfo.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'ClientPlatformInfo', + package: const $pb.PackageName(_omitMessageNames ? '' : 'defguard.proxy'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'osFamily') + ..aOS(2, _omitFieldNames ? '' : 'osType') + ..aOS(3, _omitFieldNames ? '' : 'version') + ..aOS(4, _omitFieldNames ? '' : 'edition') + ..aOS(5, _omitFieldNames ? '' : 'codename') + ..aOS(6, _omitFieldNames ? '' : 'bitness') + ..aOS(7, _omitFieldNames ? '' : 'architecture') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ClientPlatformInfo clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ClientPlatformInfo copyWith(void Function(ClientPlatformInfo) updates) => + super.copyWith((message) => updates(message as ClientPlatformInfo)) + as ClientPlatformInfo; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ClientPlatformInfo create() => ClientPlatformInfo._(); + @$core.override + ClientPlatformInfo createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static ClientPlatformInfo getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static ClientPlatformInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get osFamily => $_getSZ(0); + @$pb.TagNumber(1) + set osFamily($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasOsFamily() => $_has(0); + @$pb.TagNumber(1) + void clearOsFamily() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get osType => $_getSZ(1); + @$pb.TagNumber(2) + set osType($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasOsType() => $_has(1); + @$pb.TagNumber(2) + void clearOsType() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get version => $_getSZ(2); + @$pb.TagNumber(3) + set version($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasVersion() => $_has(2); + @$pb.TagNumber(3) + void clearVersion() => $_clearField(3); + + @$pb.TagNumber(4) + $core.String get edition => $_getSZ(3); + @$pb.TagNumber(4) + set edition($core.String value) => $_setString(3, value); + @$pb.TagNumber(4) + $core.bool hasEdition() => $_has(3); + @$pb.TagNumber(4) + void clearEdition() => $_clearField(4); + + @$pb.TagNumber(5) + $core.String get codename => $_getSZ(4); + @$pb.TagNumber(5) + set codename($core.String value) => $_setString(4, value); + @$pb.TagNumber(5) + $core.bool hasCodename() => $_has(4); + @$pb.TagNumber(5) + void clearCodename() => $_clearField(5); + + @$pb.TagNumber(6) + $core.String get bitness => $_getSZ(5); + @$pb.TagNumber(6) + set bitness($core.String value) => $_setString(5, value); + @$pb.TagNumber(6) + $core.bool hasBitness() => $_has(5); + @$pb.TagNumber(6) + void clearBitness() => $_clearField(6); + + @$pb.TagNumber(7) + $core.String get architecture => $_getSZ(6); + @$pb.TagNumber(7) + set architecture($core.String value) => $_setString(6, value); + @$pb.TagNumber(7) + $core.bool hasArchitecture() => $_has(6); + @$pb.TagNumber(7) + void clearArchitecture() => $_clearField(7); +} + +const $core.bool _omitFieldNames = + $core.bool.fromEnvironment('protobuf.omit_field_names'); +const $core.bool _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/client/lib/data/proto/client_platform_info.pbenum.dart b/client/lib/data/proto/client_platform_info.pbenum.dart new file mode 100644 index 0000000..6160acd --- /dev/null +++ b/client/lib/data/proto/client_platform_info.pbenum.dart @@ -0,0 +1,11 @@ +// This is a generated file - do not edit. +// +// Generated from client_platform_info.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports diff --git a/client/lib/data/proto/client_platform_info.pbjson.dart b/client/lib/data/proto/client_platform_info.pbjson.dart new file mode 100644 index 0000000..a30a95c --- /dev/null +++ b/client/lib/data/proto/client_platform_info.pbjson.dart @@ -0,0 +1,77 @@ +// This is a generated file - do not edit. +// +// Generated from client_platform_info.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports +// ignore_for_file: unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use clientPlatformInfoDescriptor instead') +const ClientPlatformInfo$json = { + '1': 'ClientPlatformInfo', + '2': [ + {'1': 'os_family', '3': 1, '4': 1, '5': 9, '10': 'osFamily'}, + {'1': 'os_type', '3': 2, '4': 1, '5': 9, '10': 'osType'}, + {'1': 'version', '3': 3, '4': 1, '5': 9, '10': 'version'}, + { + '1': 'edition', + '3': 4, + '4': 1, + '5': 9, + '9': 0, + '10': 'edition', + '17': true + }, + { + '1': 'codename', + '3': 5, + '4': 1, + '5': 9, + '9': 1, + '10': 'codename', + '17': true + }, + { + '1': 'bitness', + '3': 6, + '4': 1, + '5': 9, + '9': 2, + '10': 'bitness', + '17': true + }, + { + '1': 'architecture', + '3': 7, + '4': 1, + '5': 9, + '9': 3, + '10': 'architecture', + '17': true + }, + ], + '8': [ + {'1': '_edition'}, + {'1': '_codename'}, + {'1': '_bitness'}, + {'1': '_architecture'}, + ], +}; + +/// Descriptor for `ClientPlatformInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List clientPlatformInfoDescriptor = $convert.base64Decode( + 'ChJDbGllbnRQbGF0Zm9ybUluZm8SGwoJb3NfZmFtaWx5GAEgASgJUghvc0ZhbWlseRIXCgdvc1' + '90eXBlGAIgASgJUgZvc1R5cGUSGAoHdmVyc2lvbhgDIAEoCVIHdmVyc2lvbhIdCgdlZGl0aW9u' + 'GAQgASgJSABSB2VkaXRpb26IAQESHwoIY29kZW5hbWUYBSABKAlIAVIIY29kZW5hbWWIAQESHQ' + 'oHYml0bmVzcxgGIAEoCUgCUgdiaXRuZXNziAEBEicKDGFyY2hpdGVjdHVyZRgHIAEoCUgDUgxh' + 'cmNoaXRlY3R1cmWIAQFCCgoIX2VkaXRpb25CCwoJX2NvZGVuYW1lQgoKCF9iaXRuZXNzQg8KDV' + '9hcmNoaXRlY3R1cmU='); diff --git a/client/lib/data/proxy/config.dart b/client/lib/data/proxy/config.dart index 3bc931d..ffdb4d1 100644 --- a/client/lib/data/proxy/config.dart +++ b/client/lib/data/proxy/config.dart @@ -49,8 +49,8 @@ class NetworkInfoResponse { this.token, }); - factory NetworkInfoResponse.fromJson(Map json) => - _$NetworkInfoResponseFromJson(json); + factory NetworkInfoResponse.fromJson(Map json) => + _$NetworkInfoResponseFromJson(json); - Map toJson() => _$NetworkInfoResponseToJson(this); + Map toJson() => _$NetworkInfoResponseToJson(this); } diff --git a/client/lib/data/proxy/enrollment.dart b/client/lib/data/proxy/enrollment.dart index cda111f..ffa0990 100644 --- a/client/lib/data/proxy/enrollment.dart +++ b/client/lib/data/proxy/enrollment.dart @@ -152,6 +152,7 @@ class DeviceConfig { final bool mfaEnabled; final int keepaliveInterval; final LocationMfaMode? locationMfaMode; + final bool? postureCheckRequired; factory DeviceConfig.fromJson(Map json) => _$DeviceConfigFromJson(json); @@ -170,6 +171,7 @@ class DeviceConfig { required this.mfaEnabled, required this.keepaliveInterval, this.locationMfaMode, + this.postureCheckRequired, }); bool matchesLocation(Location other) { @@ -182,7 +184,8 @@ class DeviceConfig { dns == other.dns && mfaEnabled == other.mfaEnabled && keepaliveInterval == other.keepAliveInterval && - locationMfaMode == other.locationMfaMode; + locationMfaMode == other.locationMfaMode && + postureCheckRequired == other.postureCheckRequired; } LocationsCompanion toCompanion({ @@ -206,6 +209,7 @@ class DeviceConfig { allowedIps: d.Value(allowedIps), address: d.Value(assignedIp), locationMfaMode: d.Value(locationMfaMode), + postureCheckRequired: d.Value(postureCheckRequired), ); } } @@ -252,6 +256,8 @@ class InstanceInfo { final String username; final bool enterpriseEnabled; final bool disableAllTraffic; + final ClientTrafficPolicy? clientTrafficPolicy; + final String? openidDisplayName; const InstanceInfo({ required this.id, @@ -260,7 +266,10 @@ class InstanceInfo { required this.proxyUrl, required this.username, required this.enterpriseEnabled, - required this.disableAllTraffic, + // deprecated, use clientTrafficPolicy instead + @Deprecated('1.6') required this.disableAllTraffic, + required this.clientTrafficPolicy, + this.openidDisplayName, }); factory InstanceInfo.fromJson(Map json) => @@ -275,7 +284,8 @@ class InstanceInfo { proxyUrl == other.proxyUrl && username == other.username && enterpriseEnabled == other.enterpriseEnabled && - disableAllTraffic == other.disableAllTraffic; + getPolicy() == other.clientTrafficPolicy && + openidDisplayName == other.openidDisplayName; } DefguardInstancesCompanion toCompanion({DefguardInstance? instance}) { @@ -290,10 +300,19 @@ class InstanceInfo { proxyUrl: d.Value(proxyUrl), username: d.Value(username), enterpriseEnabled: d.Value(enterpriseEnabled), - disableAllTraffic: d.Value(disableAllTraffic), + clientTrafficPolicy: d.Value(getPolicy()), uuid: d.Value(id), + openidDisplayName: d.Value(openidDisplayName), ); } + + /// Retrieves `ClientTrafficPolicy` while ensuring backwards compatibility + ClientTrafficPolicy getPolicy() { + return clientTrafficPolicy ?? + (disableAllTraffic + ? ClientTrafficPolicy.disableAllTraffic + : ClientTrafficPolicy.none); + } } @JsonSerializable() diff --git a/client/lib/data/proxy/enrollment.g.dart b/client/lib/data/proxy/enrollment.g.dart index 8d83288..1eb3807 100644 --- a/client/lib/data/proxy/enrollment.g.dart +++ b/client/lib/data/proxy/enrollment.g.dart @@ -263,6 +263,10 @@ DeviceConfig _$DeviceConfigFromJson(Map json) => 'location_mfa_mode', (v) => $enumDecodeNullable(_$LocationMfaModeEnumMap, v), ), + postureCheckRequired: $checkedConvert( + 'posture_check_required', + (v) => v as bool?, + ), ); return val; }, @@ -274,6 +278,7 @@ DeviceConfig _$DeviceConfigFromJson(Map json) => 'mfaEnabled': 'mfa_enabled', 'keepaliveInterval': 'keepalive_interval', 'locationMfaMode': 'location_mfa_mode', + 'postureCheckRequired': 'posture_check_required', }, ); @@ -289,6 +294,7 @@ const _$DeviceConfigFieldMap = { 'mfaEnabled': 'mfa_enabled', 'keepaliveInterval': 'keepalive_interval', 'locationMfaMode': 'location_mfa_mode', + 'postureCheckRequired': 'posture_check_required', }; Map _$DeviceConfigToJson(DeviceConfig instance) => @@ -304,6 +310,7 @@ Map _$DeviceConfigToJson(DeviceConfig instance) => 'mfa_enabled': instance.mfaEnabled, 'keepalive_interval': instance.keepaliveInterval, 'location_mfa_mode': _$LocationMfaModeEnumMap[instance.locationMfaMode], + 'posture_check_required': instance.postureCheckRequired, }; const _$LocationMfaModeEnumMap = { @@ -389,6 +396,14 @@ InstanceInfo _$InstanceInfoFromJson(Map json) => 'disable_all_traffic', (v) => v as bool, ), + clientTrafficPolicy: $checkedConvert( + 'client_traffic_policy', + (v) => $enumDecodeNullable(_$ClientTrafficPolicyEnumMap, v), + ), + openidDisplayName: $checkedConvert( + 'openid_display_name', + (v) => v as String?, + ), ); return val; }, @@ -396,6 +411,8 @@ InstanceInfo _$InstanceInfoFromJson(Map json) => 'proxyUrl': 'proxy_url', 'enterpriseEnabled': 'enterprise_enabled', 'disableAllTraffic': 'disable_all_traffic', + 'clientTrafficPolicy': 'client_traffic_policy', + 'openidDisplayName': 'openid_display_name', }, ); @@ -407,6 +424,8 @@ const _$InstanceInfoFieldMap = { 'username': 'username', 'enterpriseEnabled': 'enterprise_enabled', 'disableAllTraffic': 'disable_all_traffic', + 'clientTrafficPolicy': 'client_traffic_policy', + 'openidDisplayName': 'openid_display_name', }; Map _$InstanceInfoToJson(InstanceInfo instance) => @@ -418,8 +437,17 @@ Map _$InstanceInfoToJson(InstanceInfo instance) => 'username': instance.username, 'enterprise_enabled': instance.enterpriseEnabled, 'disable_all_traffic': instance.disableAllTraffic, + 'client_traffic_policy': + _$ClientTrafficPolicyEnumMap[instance.clientTrafficPolicy], + 'openid_display_name': instance.openidDisplayName, }; +const _$ClientTrafficPolicyEnumMap = { + ClientTrafficPolicy.none: 0, + ClientTrafficPolicy.disableAllTraffic: 1, + ClientTrafficPolicy.forceAllTraffic: 2, +}; + AppInfoResponse _$AppInfoResponseFromJson(Map json) => $checkedCreate('AppInfoResponse', json, ($checkedConvert) { final val = AppInfoResponse( diff --git a/client/lib/data/proxy/mfa.dart b/client/lib/data/proxy/mfa.dart index 55949ab..a760191 100644 --- a/client/lib/data/proxy/mfa.dart +++ b/client/lib/data/proxy/mfa.dart @@ -1,5 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:mobile/data/db/enums.dart'; +import 'package:mobile/enterprise/postures.dart'; part 'mfa.g.dart'; @@ -8,11 +9,13 @@ class StartMfaRequest { final String pubkey; final int locationId; final MfaMethod method; + final DevicePostureData? postureData; const StartMfaRequest({ required this.pubkey, required this.locationId, required this.method, + this.postureData, }); factory StartMfaRequest.fromJson(Map json) => diff --git a/client/lib/data/proxy/mfa.g.dart b/client/lib/data/proxy/mfa.g.dart index b439a83..3b09b30 100644 --- a/client/lib/data/proxy/mfa.g.dart +++ b/client/lib/data/proxy/mfa.g.dart @@ -7,22 +7,37 @@ part of 'mfa.dart'; // ************************************************************************** StartMfaRequest _$StartMfaRequestFromJson(Map json) => - $checkedCreate('StartMfaRequest', json, ($checkedConvert) { - final val = StartMfaRequest( - pubkey: $checkedConvert('pubkey', (v) => v as String), - locationId: $checkedConvert('location_id', (v) => (v as num).toInt()), - method: $checkedConvert( - 'method', - (v) => $enumDecode(_$MfaMethodEnumMap, v), - ), - ); - return val; - }, fieldKeyMap: const {'locationId': 'location_id'}); + $checkedCreate( + 'StartMfaRequest', + json, + ($checkedConvert) { + final val = StartMfaRequest( + pubkey: $checkedConvert('pubkey', (v) => v as String), + locationId: $checkedConvert('location_id', (v) => (v as num).toInt()), + method: $checkedConvert( + 'method', + (v) => $enumDecode(_$MfaMethodEnumMap, v), + ), + postureData: $checkedConvert( + 'posture_data', + (v) => v == null + ? null + : DevicePostureData.fromJson(v as Map), + ), + ); + return val; + }, + fieldKeyMap: const { + 'locationId': 'location_id', + 'postureData': 'posture_data', + }, + ); const _$StartMfaRequestFieldMap = { 'pubkey': 'pubkey', 'locationId': 'location_id', 'method': 'method', + 'postureData': 'posture_data', }; Map _$StartMfaRequestToJson(StartMfaRequest instance) => @@ -30,6 +45,7 @@ Map _$StartMfaRequestToJson(StartMfaRequest instance) => 'pubkey': instance.pubkey, 'location_id': instance.locationId, 'method': _$MfaMethodEnumMap[instance.method]!, + 'posture_data': instance.postureData, }; const _$MfaMethodEnumMap = { diff --git a/client/lib/enterprise/config_update.dart b/client/lib/enterprise/config_update.dart index 91c7107..d2360f6 100644 --- a/client/lib/enterprise/config_update.dart +++ b/client/lib/enterprise/config_update.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:mobile/data/db/database.dart'; +import 'package:mobile/data/db/enums.dart'; import 'package:mobile/open/api.dart'; import 'package:mobile/open/widgets/toaster/toast_manager.dart'; import 'package:mobile/utils/update_instance.dart'; @@ -43,8 +44,7 @@ class ConfigurationUpdater extends HookConsumerWidget { ); for (final instance in instances) { talker.debug( - "Auto configuration update started for ${instance.name} (${instance - .id})", + "Auto configuration update started for ${instance.name} (${instance.id})", ); final (responseData, responseStatus, headers) = await proxyApi .pollConfiguration(instance.proxyUrl, instance.poolingToken); @@ -60,8 +60,7 @@ class ConfigurationUpdater extends HookConsumerWidget { headers['defguard-component-version']?.first; if (coreVersionStr == null || proxyVersionStr == null) { talker.error( - "Version headers missing for ${instance - .logName}, treating as unsupported", + "Version headers missing for ${instance.logName}, treating as unsupported", ); versionUnsupportedInstances.add({ 'name': instance.name, @@ -86,7 +85,7 @@ class ConfigurationUpdater extends HookConsumerWidget { // instance lost it's enterprise status if (responseStatus == 402) { final instanceUpdate = instance.copyWith( - disableAllTraffic: false, + clientTrafficPolicy: ClientTrafficPolicy.none, enterpriseEnabled: false, ); await db.managers.defguardInstances.replace(instanceUpdate); @@ -94,9 +93,7 @@ class ConfigurationUpdater extends HookConsumerWidget { } if (responseData == null) { talker.error( - "Auto configuration update failed for ${instance - .logName} ! Update data retrieval failed, status: ${responseStatus ?? - "unknown"}!", + "Auto configuration update failed for ${instance.logName} ! Update data retrieval failed, status: ${responseStatus ?? "unknown"}!", ); continue; } @@ -111,12 +108,7 @@ class ConfigurationUpdater extends HookConsumerWidget { ); if (updateResult != null) { talker.info( - "Instance ${instance - .logName} results: Instance updated: ${updateResult - .instanceChanged} | Locations updated: ${updateResult - .locationsUpdated} | Locations removed: ${updateResult - .locationsRemoved} | Locations added: ${updateResult - .locationsAdded}", + "Instance ${instance.logName} results: Instance updated: ${updateResult.instanceChanged} | Locations updated: ${updateResult.locationsUpdated} | Locations removed: ${updateResult.locationsRemoved} | Locations added: ${updateResult.locationsAdded}", ); if (updateResult.didChange) { final message = getInstanceUpdateMessage( @@ -143,7 +135,7 @@ class ConfigurationUpdater extends HookConsumerWidget { "The following instances have versions that are incompatible with your Defguard Mobile Client and may not work correctly:\n\n"; for (final instance in versionUnsupportedInstances) { message += - "- ${instance['name']}: Defguard Core ${instance['coreVersion']} (expected >=$supportedCoreVersion), Defguard Proxy ${instance['proxyVersion']} (expected >=$supportedProxyVersion)\n"; + "- ${instance['name']}: Defguard Core ${instance['coreVersion']} (expected >=$supportedCoreVersion), Defguard Proxy ${instance['proxyVersion']} (expected >=$supportedProxyVersion)\n"; } message += "\nPlease contact your administrator."; toaster.showInfo( @@ -169,10 +161,12 @@ class ConfigurationUpdater extends HookConsumerWidget { // update when user wakes up application useEffect(() { final timeTick = DateTime.now(); - final afterCooldown = lastConfigUpdate.value == null || + final afterCooldown = + lastConfigUpdate.value == null || (lastConfigUpdate.value != null && - lastConfigUpdate.value!.add(Duration(seconds: 60)).isBefore( - timeTick)); + lastConfigUpdate.value! + .add(Duration(seconds: 60)) + .isBefore(timeTick)); if (lifecycle == AppLifecycleState.resumed && afterCooldown) { lastConfigUpdate.value = timeTick; updateConfiguration(); diff --git a/client/lib/enterprise/postures.dart b/client/lib/enterprise/postures.dart new file mode 100644 index 0000000..d4d0447 --- /dev/null +++ b/client/lib/enterprise/postures.dart @@ -0,0 +1,220 @@ +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +part 'postures.g.dart'; + +enum UnavailableReason { + unspecified(0), + insufficientPermissions(1), + notApplicable(2), + detectionFailed(3); + + final int value; + + const UnavailableReason(this.value); +} + +@JsonSerializable() +class StringCheck { + final Map result; + + const StringCheck({required this.result}); + + factory StringCheck.value(String value) => + StringCheck(result: {'Value': value}); + + factory StringCheck.unavailable(UnavailableReason reason) => + StringCheck(result: {'Unavailable': reason.value}); + + factory StringCheck.fromJson(Map json) => + _$StringCheckFromJson(json); + + Map toJson() => _$StringCheckToJson(this); +} + +@JsonSerializable() +class BoolCheck { + final Map result; + + const BoolCheck({required this.result}); + + factory BoolCheck.value(bool value) => BoolCheck(result: {'Value': value}); + + factory BoolCheck.unavailable(UnavailableReason reason) => + BoolCheck(result: {'Unavailable': reason.value}); + + factory BoolCheck.fromJson(Map json) => + _$BoolCheckFromJson(json); + + Map toJson() => _$BoolCheckToJson(this); +} + +@JsonSerializable() +class Int32Check { + final Map result; + + const Int32Check({required this.result}); + + factory Int32Check.value(int value) => Int32Check(result: {'Value': value}); + + factory Int32Check.unavailable(UnavailableReason reason) => + Int32Check(result: {'Unavailable': reason.value}); + + factory Int32Check.fromJson(Map json) => + _$Int32CheckFromJson(json); + + Map toJson() => _$Int32CheckToJson(this); +} + +@JsonSerializable() +class DevicePostureData { + final String defguardClientVersion; + final String osType; + final StringCheck? osName; + final StringCheck? osVersion; + final BoolCheck? diskEncryption; + final BoolCheck? antivirusPresent; + final BoolCheck? windowsAdDomainJoined; + final Int32Check? windowsSecurityUpdateAgeDays; + final StringCheck? linuxKernelVersion; + final BoolCheck? deviceIntegrity; + final StringCheck? androidSecurityPatchDate; + + const DevicePostureData({ + required this.defguardClientVersion, + required this.osType, + this.osName, + this.osVersion, + this.diskEncryption, + this.antivirusPresent, + this.windowsAdDomainJoined, + this.windowsSecurityUpdateAgeDays, + this.linuxKernelVersion, + this.deviceIntegrity, + this.androidSecurityPatchDate, + }); + + factory DevicePostureData.fromJson(Map json) => + _$DevicePostureDataFromJson(json); + + Map toJson() => _$DevicePostureDataToJson(this); +} + +@JsonSerializable() +class PostureConnectRequest { + final int locationId; + final String pubkey; + final DevicePostureData devicePostureData; + final String token; + + const PostureConnectRequest({ + required this.locationId, + required this.pubkey, + required this.devicePostureData, + required this.token, + }); + + factory PostureConnectRequest.fromJson(Map json) => + _$PostureConnectRequestFromJson(json); + + Map toJson() => _$PostureConnectRequestToJson(this); +} + +@JsonSerializable() +class PostureConnectResponse { + final String presharedKey; + + const PostureConnectResponse({required this.presharedKey}); + + factory PostureConnectResponse.fromJson(Map json) => + _$PostureConnectResponseFromJson(json); + + Map toJson() => _$PostureConnectResponseToJson(this); +} + +Future getPosture() async { + final packageInfo = await PackageInfo.fromPlatform(); + final deviceInfo = DeviceInfoPlugin(); + + // Handle Android + if (Platform.isAndroid) { + final android = await deviceInfo.androidInfo; + return DevicePostureData( + defguardClientVersion: packageInfo.version, + osType: "Android", + osName: StringCheck.value(android.version.release), + osVersion: StringCheck.value(android.version.release), + // TODO: implement full google play integrity check flow + // TODO: https://github.com/DefGuard/defguard/issues/2986 + deviceIntegrity: BoolCheck.unavailable(UnavailableReason.unspecified), + + diskEncryption: BoolCheck.unavailable(UnavailableReason.notApplicable), + antivirusPresent: BoolCheck.unavailable(UnavailableReason.notApplicable), + windowsAdDomainJoined: BoolCheck.unavailable( + UnavailableReason.notApplicable, + ), + windowsSecurityUpdateAgeDays: Int32Check.unavailable( + UnavailableReason.notApplicable, + ), + linuxKernelVersion: StringCheck.unavailable( + UnavailableReason.notApplicable, + ), + androidSecurityPatchDate: android.version.securityPatch != null + ? StringCheck.value(android.version.securityPatch!) + : StringCheck.unavailable(UnavailableReason.detectionFailed), + ); + } + + // Handle iOS + if (Platform.isIOS) { + final ios = await deviceInfo.iosInfo; + return DevicePostureData( + defguardClientVersion: packageInfo.version, + osType: "iOS", + osName: StringCheck.value(ios.systemName), + osVersion: StringCheck.value(ios.systemVersion), + deviceIntegrity: BoolCheck.unavailable(UnavailableReason.notApplicable), + + diskEncryption: BoolCheck.unavailable(UnavailableReason.notApplicable), + antivirusPresent: BoolCheck.unavailable(UnavailableReason.notApplicable), + windowsAdDomainJoined: BoolCheck.unavailable( + UnavailableReason.notApplicable, + ), + windowsSecurityUpdateAgeDays: Int32Check.unavailable( + UnavailableReason.notApplicable, + ), + linuxKernelVersion: StringCheck.unavailable( + UnavailableReason.notApplicable, + ), + androidSecurityPatchDate: StringCheck.unavailable( + UnavailableReason.notApplicable, + ), + ); + } + + // Fallback for unsupported platforms: report the generic Dart OS values + return DevicePostureData( + defguardClientVersion: packageInfo.version, + osType: Platform.operatingSystem, + osName: StringCheck.value(Platform.operatingSystem), + osVersion: StringCheck.unavailable(UnavailableReason.unspecified), + diskEncryption: BoolCheck.unavailable(UnavailableReason.notApplicable), + antivirusPresent: BoolCheck.unavailable(UnavailableReason.notApplicable), + windowsAdDomainJoined: BoolCheck.unavailable( + UnavailableReason.notApplicable, + ), + windowsSecurityUpdateAgeDays: Int32Check.unavailable( + UnavailableReason.notApplicable, + ), + linuxKernelVersion: StringCheck.unavailable( + UnavailableReason.notApplicable, + ), + deviceIntegrity: BoolCheck.unavailable(UnavailableReason.notApplicable), + androidSecurityPatchDate: StringCheck.unavailable( + UnavailableReason.notApplicable, + ), + ); +} diff --git a/client/lib/enterprise/postures.g.dart b/client/lib/enterprise/postures.g.dart new file mode 100644 index 0000000..974c789 --- /dev/null +++ b/client/lib/enterprise/postures.g.dart @@ -0,0 +1,208 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'postures.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +StringCheck _$StringCheckFromJson(Map json) => + $checkedCreate('StringCheck', json, ($checkedConvert) { + final val = StringCheck( + result: $checkedConvert('result', (v) => v as Map), + ); + return val; + }); + +const _$StringCheckFieldMap = {'result': 'result'}; + +Map _$StringCheckToJson(StringCheck instance) => + {'result': instance.result}; + +BoolCheck _$BoolCheckFromJson(Map json) => + $checkedCreate('BoolCheck', json, ($checkedConvert) { + final val = BoolCheck( + result: $checkedConvert('result', (v) => v as Map), + ); + return val; + }); + +const _$BoolCheckFieldMap = {'result': 'result'}; + +Map _$BoolCheckToJson(BoolCheck instance) => { + 'result': instance.result, +}; + +Int32Check _$Int32CheckFromJson(Map json) => + $checkedCreate('Int32Check', json, ($checkedConvert) { + final val = Int32Check( + result: $checkedConvert('result', (v) => v as Map), + ); + return val; + }); + +const _$Int32CheckFieldMap = {'result': 'result'}; + +Map _$Int32CheckToJson(Int32Check instance) => + {'result': instance.result}; + +DevicePostureData _$DevicePostureDataFromJson( + Map json, +) => $checkedCreate( + 'DevicePostureData', + json, + ($checkedConvert) { + final val = DevicePostureData( + defguardClientVersion: $checkedConvert( + 'defguard_client_version', + (v) => v as String, + ), + osType: $checkedConvert('os_type', (v) => v as String), + osName: $checkedConvert( + 'os_name', + (v) => + v == null ? null : StringCheck.fromJson(v as Map), + ), + osVersion: $checkedConvert( + 'os_version', + (v) => + v == null ? null : StringCheck.fromJson(v as Map), + ), + diskEncryption: $checkedConvert( + 'disk_encryption', + (v) => v == null ? null : BoolCheck.fromJson(v as Map), + ), + antivirusPresent: $checkedConvert( + 'antivirus_present', + (v) => v == null ? null : BoolCheck.fromJson(v as Map), + ), + windowsAdDomainJoined: $checkedConvert( + 'windows_ad_domain_joined', + (v) => v == null ? null : BoolCheck.fromJson(v as Map), + ), + windowsSecurityUpdateAgeDays: $checkedConvert( + 'windows_security_update_age_days', + (v) => + v == null ? null : Int32Check.fromJson(v as Map), + ), + linuxKernelVersion: $checkedConvert( + 'linux_kernel_version', + (v) => + v == null ? null : StringCheck.fromJson(v as Map), + ), + deviceIntegrity: $checkedConvert( + 'device_integrity', + (v) => v == null ? null : BoolCheck.fromJson(v as Map), + ), + androidSecurityPatchDate: $checkedConvert( + 'android_security_patch_date', + (v) => + v == null ? null : StringCheck.fromJson(v as Map), + ), + ); + return val; + }, + fieldKeyMap: const { + 'defguardClientVersion': 'defguard_client_version', + 'osType': 'os_type', + 'osName': 'os_name', + 'osVersion': 'os_version', + 'diskEncryption': 'disk_encryption', + 'antivirusPresent': 'antivirus_present', + 'windowsAdDomainJoined': 'windows_ad_domain_joined', + 'windowsSecurityUpdateAgeDays': 'windows_security_update_age_days', + 'linuxKernelVersion': 'linux_kernel_version', + 'deviceIntegrity': 'device_integrity', + 'androidSecurityPatchDate': 'android_security_patch_date', + }, +); + +const _$DevicePostureDataFieldMap = { + 'defguardClientVersion': 'defguard_client_version', + 'osType': 'os_type', + 'osName': 'os_name', + 'osVersion': 'os_version', + 'diskEncryption': 'disk_encryption', + 'antivirusPresent': 'antivirus_present', + 'windowsAdDomainJoined': 'windows_ad_domain_joined', + 'windowsSecurityUpdateAgeDays': 'windows_security_update_age_days', + 'linuxKernelVersion': 'linux_kernel_version', + 'deviceIntegrity': 'device_integrity', + 'androidSecurityPatchDate': 'android_security_patch_date', +}; + +Map _$DevicePostureDataToJson(DevicePostureData instance) => + { + 'defguard_client_version': instance.defguardClientVersion, + 'os_type': instance.osType, + 'os_name': instance.osName, + 'os_version': instance.osVersion, + 'disk_encryption': instance.diskEncryption, + 'antivirus_present': instance.antivirusPresent, + 'windows_ad_domain_joined': instance.windowsAdDomainJoined, + 'windows_security_update_age_days': instance.windowsSecurityUpdateAgeDays, + 'linux_kernel_version': instance.linuxKernelVersion, + 'device_integrity': instance.deviceIntegrity, + 'android_security_patch_date': instance.androidSecurityPatchDate, + }; + +PostureConnectRequest _$PostureConnectRequestFromJson( + Map json, +) => $checkedCreate( + 'PostureConnectRequest', + json, + ($checkedConvert) { + final val = PostureConnectRequest( + locationId: $checkedConvert('location_id', (v) => (v as num).toInt()), + pubkey: $checkedConvert('pubkey', (v) => v as String), + devicePostureData: $checkedConvert( + 'device_posture_data', + (v) => DevicePostureData.fromJson(v as Map), + ), + token: $checkedConvert('token', (v) => v as String), + ); + return val; + }, + fieldKeyMap: const { + 'locationId': 'location_id', + 'devicePostureData': 'device_posture_data', + }, +); + +const _$PostureConnectRequestFieldMap = { + 'locationId': 'location_id', + 'pubkey': 'pubkey', + 'devicePostureData': 'device_posture_data', + 'token': 'token', +}; + +Map _$PostureConnectRequestToJson( + PostureConnectRequest instance, +) => { + 'location_id': instance.locationId, + 'pubkey': instance.pubkey, + 'device_posture_data': instance.devicePostureData, + 'token': instance.token, +}; + +PostureConnectResponse _$PostureConnectResponseFromJson( + Map json, +) => $checkedCreate( + 'PostureConnectResponse', + json, + ($checkedConvert) { + final val = PostureConnectResponse( + presharedKey: $checkedConvert('preshared_key', (v) => v as String), + ); + return val; + }, + fieldKeyMap: const {'presharedKey': 'preshared_key'}, +); + +const _$PostureConnectResponseFieldMap = { + 'presharedKey': 'preshared_key', +}; + +Map _$PostureConnectResponseToJson( + PostureConnectResponse instance, +) => {'preshared_key': instance.presharedKey}; diff --git a/client/lib/enterprise/screens/mfa/openid_mfa_screen.dart b/client/lib/enterprise/screens/mfa/openid_mfa_screen.dart index 2eb337a..7d4cc69 100644 --- a/client/lib/enterprise/screens/mfa/openid_mfa_screen.dart +++ b/client/lib/enterprise/screens/mfa/openid_mfa_screen.dart @@ -17,18 +17,28 @@ import '../../../open/services/snackbar_service.dart'; class OpenIdMfaScreenData { final String proxyUrl; final String token; + final String? openidDisplayName; - const OpenIdMfaScreenData({required this.proxyUrl, required this.token}); + const OpenIdMfaScreenData({ + required this.proxyUrl, + required this.token, + this.openidDisplayName, + }); } final String _title = "Two-factor authentication"; -final String _mfaMsg1 = - "In order to connect to VPN please login with your OpenID provider. To do so, please click \"Authenticate with OpenId\""; +String _mfaMsg1(String? providerName) { + final name = providerName ?? 'OpenID'; + return "In order to connect to VPN please login with $name. To do so, please click \"Authenticate with $name\" button below"; +} -final String _mfaMsg2 = - "This will open a new window in your web browser and automatically redirect you to your OpenID provider login page. After authenticating please get back here"; +String _mfaMsg2(String? providerName) { + final name = providerName ?? 'OpenID'; + return "This will open a new window in your Web Browser and automatically redirect you to the $name login page. After authenticating with $name please get back here"; +} -final String _authenticateMsg = "Authenticate with OpenID"; +String _authenticateMsg(String? providerName) => + 'Authenticate with ${providerName ?? 'OpenID'}'; class OpenIdMfaScreen extends HookConsumerWidget { final OpenIdMfaScreenData screenData; @@ -67,17 +77,17 @@ class OpenIdMfaScreen extends HookConsumerWidget { ), Center(child: DgIconOpenidOpen(size: 128)), Text( - _mfaMsg1, + _mfaMsg1(screenData.openidDisplayName), style: DgText.modal1.copyWith(color: DgColor.textBodySecondary), textAlign: TextAlign.center, ), Text( - _mfaMsg2, + _mfaMsg2(screenData.openidDisplayName), style: DgText.modal1.copyWith(color: DgColor.textBodySecondary), textAlign: TextAlign.center, ), DgButton( - text: _authenticateMsg, + text: _authenticateMsg(screenData.openidDisplayName), variant: DgButtonVariant.primary, size: DgButtonSize.big, width: double.infinity, diff --git a/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart b/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart index 6d12012..3970b96 100644 --- a/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart +++ b/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart @@ -13,6 +13,7 @@ import 'package:mobile/theme/text.dart'; import '../../../../../logging.dart'; import '../../../open/services/snackbar_service.dart'; +import '../../../utils/error_handler.dart'; class OpenIdMfaWaitingScreenData { final String proxyUrl; @@ -51,8 +52,18 @@ class OpenIdMfaWaitingScreen extends HookConsumerWidget { final response = await proxyApi.finishMfa(uri, request); return response; } on DioException catch (e) { - if (e.response?.statusCode == 428) { - talker.debug("User did not complete openid browser login, waiting"); + final isNetworkError = + e.type == DioExceptionType.connectionError || + e.type == DioExceptionType.connectionTimeout || + (e.error?.toString().contains("-1005") ?? false) || + (e.message?.contains("-1005") ?? false); + + if (e.response?.statusCode == 428 || isNetworkError) { + if (isNetworkError) { + talker.warning("Network error during MFA polling, retrying: $e"); + } else { + talker.debug("User did not complete openid browser login, waiting"); + } await Future.delayed(Duration(seconds: 2)); } else { rethrow; @@ -83,8 +94,9 @@ class OpenIdMfaWaitingScreen extends HookConsumerWidget { }) .catchError((error) { talker.error("OpenID MFA polling error: $error"); + final message = ErrorHandler.getHumanReadableError(error); SnackbarService.show( - "Error: $error", + message, textColor: DgColor.textAlert, dismissable: true, ); diff --git a/client/lib/logging.dart b/client/lib/logging.dart index f1284bb..8d26881 100644 --- a/client/lib/logging.dart +++ b/client/lib/logging.dart @@ -1,3 +1,3 @@ import 'package:talker_flutter/talker_flutter.dart'; -final talker = TalkerFlutter.init(); \ No newline at end of file +final talker = TalkerFlutter.init(); diff --git a/client/lib/open/api.dart b/client/lib/open/api.dart index 6b996bd..60870d6 100644 --- a/client/lib/open/api.dart +++ b/client/lib/open/api.dart @@ -1,13 +1,18 @@ +import 'dart:convert'; import 'dart:io'; import 'package:cookie_jar/cookie_jar.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:mobile/data/db/enums.dart'; +import 'package:mobile/data/proto/client_platform_info.pb.dart'; import 'package:mobile/data/proxy/config.dart'; import 'package:mobile/data/proxy/enrollment.dart'; import 'package:mobile/data/proxy/mfa.dart'; - +import 'package:mobile/enterprise/postures.dart'; +import 'package:native_dio_adapter/native_dio_adapter.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:talker_dio_logger/talker_dio_logger_interceptor.dart'; import '../logging.dart'; @@ -15,6 +20,16 @@ import '../logging.dart'; const _apiV1Segments = ['api', 'v1']; final enrollmentPathSegments = ['api', 'v1', 'enrollment']; final mfaPathSegments = ['api', 'v1', 'client-mfa']; +final posturePathSegments = ['api', 'v1', 'posture']; + +class PostureCheckException implements Exception { + final String message; + + const PostureCheckException(this.message); + + @override + String toString() => 'Posture error: $message'; +} class MfaMethodNotAvailableException implements Exception { final MfaMethod method; @@ -41,9 +56,54 @@ class _ProxyApi { ); _ProxyApi._internal() { + _dio.httpClientAdapter = NativeAdapter(); final cookieJar = CookieJar(); _dio.interceptors.add(CookieManager(cookieJar)); _dio.interceptors.add(TalkerDioLogger(talker: talker)); + _initHeaders(); + } + + Future _initHeaders() async { + try { + final deviceInfo = DeviceInfoPlugin(); + final ClientPlatformInfo platformInfo; + + if (Platform.isAndroid) { + final android = await deviceInfo.androidInfo; + platformInfo = ClientPlatformInfo( + osFamily: 'android', + osType: 'Android', + version: android.version.release, + codename: android.version.codename, + architecture: android.supportedAbis.first, + bitness: '64', + ); + } else if (Platform.isIOS) { + final ios = await deviceInfo.iosInfo; + platformInfo = ClientPlatformInfo( + osFamily: 'ios', + osType: 'iOS', + version: ios.systemVersion, + architecture: 'arm64', + bitness: '64', + ); + } else { + platformInfo = ClientPlatformInfo( + osFamily: Platform.operatingSystem, + osType: Platform.operatingSystem, + version: Platform.operatingSystemVersion, + ); + } + + final platformBytes = platformInfo.writeToBuffer(); + final platformBase64 = base64Encode(platformBytes); + + final packageInfo = await PackageInfo.fromPlatform(); + _dio.options.headers['defguard-client-version'] = packageInfo.version; + _dio.options.headers['defguard-client-platform'] = platformBase64; + } catch (e) { + talker.error("Failed to set client headers", e); + } } Future<(ConfigurationPollResponse?, int?, Headers?)> pollConfiguration( @@ -152,6 +212,13 @@ class _ProxyApi { dataError.toLowerCase().trim() == missingMFAMethodError) { throw MfaMethodNotAvailableException(data.method); } + + if (e.response?.statusCode == 403) { + final error = responseData['error'] ?? responseData['message']; + if (error is String) { + throw HttpException(error); + } + } } throw HttpException( "Failed to start MFA. Status: ${e.response?.statusCode} Body: ${e.response?.data}", @@ -165,6 +232,39 @@ class _ProxyApi { } } + Future postureConnect( + Uri url, + PostureConnectRequest data, + ) async { + final endpoint = url.replace( + pathSegments: [...url.pathSegments, ...posturePathSegments, 'connect'], + ); + + try { + final response = await _dio.postUri(endpoint, data: data.toJson()); + return PostureConnectResponse.fromJson(response.data); + } on DioException catch (e) { + final responseData = e.response?.data; + if (e.response?.statusCode == 403 && + responseData is Map) { + final error = responseData['error'] ?? responseData['message']; + if (error is String) { + throw PostureCheckException(error); + } + } + if (e.response != null) { + throw HttpException( + 'Failed to perform posture check. Status: ${e.response?.statusCode} Body: ${e.response?.data}', + ); + } + rethrow; + } catch (e) { + throw FormatException( + 'Invalid JSON sent by posture check endpoint! Error: $e', + ); + } + } + Future finishMfa(Uri url, FinishMfaRequest data) async { final endpoint = url.replace( pathSegments: [...url.pathSegments, ...mfaPathSegments, 'finish'], diff --git a/client/lib/open/riverpod/biometrics_state.dart b/client/lib/open/riverpod/biometrics_state.dart index 8b5ddd6..7c5421a 100644 --- a/client/lib/open/riverpod/biometrics_state.dart +++ b/client/lib/open/riverpod/biometrics_state.dart @@ -7,7 +7,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'biometrics_state.g.dart'; - class BiometricsState { bool isSupported; bool canCheck; diff --git a/client/lib/open/riverpod/package_info/package_info.dart b/client/lib/open/riverpod/package_info/package_info.dart index bf6a28e..d46691a 100644 --- a/client/lib/open/riverpod/package_info/package_info.dart +++ b/client/lib/open/riverpod/package_info/package_info.dart @@ -10,4 +10,4 @@ Future packageInfo(Ref ref) async { WidgetsFlutterBinding.ensureInitialized(); final info = await PackageInfo.fromPlatform(); return info; -} \ No newline at end of file +} diff --git a/client/lib/open/riverpod/plugin/plugin.dart b/client/lib/open/riverpod/plugin/plugin.dart index 2d7b177..4f2f6e0 100644 --- a/client/lib/open/riverpod/plugin/plugin.dart +++ b/client/lib/open/riverpod/plugin/plugin.dart @@ -15,4 +15,4 @@ class PluginActiveTunnelState extends _$PluginActiveTunnelState { void clear() { state = null; } -} \ No newline at end of file +} diff --git a/client/lib/open/screens/add_instance/add_instance_screen.dart b/client/lib/open/screens/add_instance/add_instance_screen.dart index df16e36..6c1a27b 100644 --- a/client/lib/open/screens/add_instance/add_instance_screen.dart +++ b/client/lib/open/screens/add_instance/add_instance_screen.dart @@ -77,9 +77,7 @@ class AddInstanceScreen extends HookConsumerWidget { if (isAgreed ?? false) { if (context.mounted) { QRScreenRoute( - QrScreenData( - intent: QrScreenIntent.addInstance, - ), + QrScreenData(intent: QrScreenIntent.addInstance), ).push(context); } } else { @@ -92,9 +90,7 @@ class AddInstanceScreen extends HookConsumerWidget { await asyncPrefs.setBool(agreementPrefsKey, true); if (context.mounted) { QRScreenRoute( - QrScreenData( - intent: QrScreenIntent.addInstance, - ), + QrScreenData(intent: QrScreenIntent.addInstance), ).push(context); } } diff --git a/client/lib/open/screens/add_instance/generate_wireguard.dart b/client/lib/open/screens/add_instance/generate_wireguard.dart index f9cd791..61c7d5d 100644 --- a/client/lib/open/screens/add_instance/generate_wireguard.dart +++ b/client/lib/open/screens/add_instance/generate_wireguard.dart @@ -3,14 +3,10 @@ import 'dart:convert'; import 'package:mobile/data/proxy/enrollment.dart'; import 'package:x25519/x25519.dart' as x; - Future generateWireguardKeyPair() async { final keyPair = x.generateKeyPair(); final encodedPriv = base64Encode(keyPair.privateKey); final encodedPub = base64Encode(keyPair.publicKey); - return WireguardEncodedKeyPair( - privKey: encodedPriv, - pubKey: encodedPub, - ); + return WireguardEncodedKeyPair(privKey: encodedPriv, pubKey: encodedPub); } diff --git a/client/lib/open/screens/add_instance/screens/name_device_screen.dart b/client/lib/open/screens/add_instance/screens/name_device_screen.dart index a3ce5ce..6e25082 100644 --- a/client/lib/open/screens/add_instance/screens/name_device_screen.dart +++ b/client/lib/open/screens/add_instance/screens/name_device_screen.dart @@ -58,12 +58,15 @@ class NameDeviceScreen extends HookConsumerWidget { uuid: createResponse.instance.id, deviceId: createResponse.device.id, enterpriseEnabled: createResponse.instance.enterpriseEnabled, - disableAllTraffic: createResponse.instance.disableAllTraffic, + clientTrafficPolicy: drift.Value(createResponse.instance.getPolicy()), proxyUrl: createResponse.instance.proxyUrl, url: screenData.startResponse.instance.url, username: createResponse.instance.username, poolingToken: createResponse.token, mfaKeysStored: false, + openidDisplayName: drift.Value( + createResponse.instance.openidDisplayName, + ), ), mode: drift.InsertMode.insertOrFail, ); @@ -101,7 +104,7 @@ class NameDeviceScreen extends HookConsumerWidget { suggestedName = ""; } nameController.text = suggestedName; - } catch(e) { + } catch (e) { talker.error("Failed to get suggested device name! Reason: $e"); } } diff --git a/client/lib/open/screens/instance/instance_screen.dart b/client/lib/open/screens/instance/instance_screen.dart index 1197462..310acef 100644 --- a/client/lib/open/screens/instance/instance_screen.dart +++ b/client/lib/open/screens/instance/instance_screen.dart @@ -415,7 +415,7 @@ class _LocationItem extends HookConsumerWidget { ); }, ), - if (!instance.disableAllTraffic) + if (instance.clientTrafficPolicy == ClientTrafficPolicy.none) DgMenuItem( text: "Select Traffic Routing", onTap: () { @@ -424,6 +424,7 @@ class _LocationItem extends HookConsumerWidget { builder: (_) => RoutingMethodDialog( location: location, intention: RoutingMethodDialogIntention.save, + clientTrafficPolicy: instance.clientTrafficPolicy, ), ); }, diff --git a/client/lib/open/screens/instance/services/tunnel_service.dart b/client/lib/open/screens/instance/services/tunnel_service.dart index e156917..62ab24d 100644 --- a/client/lib/open/screens/instance/services/tunnel_service.dart +++ b/client/lib/open/screens/instance/services/tunnel_service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:mobile/data/db/database.dart'; import 'package:mobile/data/proxy/mfa.dart'; +import 'package:mobile/enterprise/postures.dart'; import 'package:mobile/enterprise/screens/mfa/openid_mfa_screen.dart'; import 'package:mobile/open/api.dart'; import 'package:mobile/data/plugin/plugin.dart'; @@ -35,9 +36,13 @@ class TunnelService { // handle traffic type selection if necessary late RoutingMethod trafficMethod; - if (instance.disableAllTraffic) { + if (instance.clientTrafficPolicy == ClientTrafficPolicy.disableAllTraffic) { // instance enforces predefined traffic trafficMethod = RoutingMethod.predefined; + } else if (instance.clientTrafficPolicy == + ClientTrafficPolicy.forceAllTraffic) { + // instance enforces all traffic + trafficMethod = RoutingMethod.all; } else { // instance allows traffic type selection - use stored method or display selection dialog if (location.trafficMethod != null) { @@ -52,6 +57,7 @@ class TunnelService { builder: (_) => RoutingMethodDialog( location: location, intention: dialogIntention, + clientTrafficPolicy: instance.clientTrafficPolicy, ), ); // smth went wrong or user canceled the operation @@ -107,12 +113,24 @@ class TunnelService { payload: payload, method: mfaMethod, secureStorageKey: instance.secureStorageKey, + openidDisplayName: instance.openidDisplayName, ); if (presharedKey == null) { // user dismissed the dialog return; } payload.presharedKey = presharedKey; + } else if (payload.postureCheckRequired) { + final presharedKey = await _performPostureCheck( + navigator: navigator, + proxyUrl: instance.proxyUrl, + payload: payload, + pollingToken: instance.poolingToken, + ); + if (presharedKey == null) { + return; + } + payload.presharedKey = presharedKey; } // start the tunnel @@ -127,6 +145,40 @@ class TunnelService { location.locationMfaMode == LocationMfaMode.external; } + /// Performs posture-only authorization and returns runtime preshared key. + static Future _performPostureCheck({ + required NavigatorState navigator, + required String proxyUrl, + required PluginConnectPayload payload, + required String pollingToken, + }) async { + final messenger = ScaffoldMessenger.of(navigator.context); + try { + return await _authorizePostureOnly( + proxyUrl, + payload.devicePublicKey, + payload.networkId, + pollingToken, + ); + } on PostureCheckException catch (e) { + talker.error('Posture check failed', e); + messenger.showSnackBar( + dgSnackBar(text: e.toString(), textColor: DgColor.textAlert), + ); + } on HttpException catch (e) { + talker.error('Posture check request failed', e); + messenger.showSnackBar( + dgSnackBar(text: 'Error: ${e.message}', textColor: DgColor.textAlert), + ); + } catch (e) { + talker.error('Posture-only connect failed: $e'); + messenger.showSnackBar( + dgSnackBar(text: 'Error: $e', textColor: DgColor.textAlert), + ); + } + return null; + } + /// Performs MFA using specified method. /// Returns preshared key. static Future _performMfa({ @@ -135,6 +187,7 @@ class TunnelService { required PluginConnectPayload payload, required MfaMethod method, String? secureStorageKey, + String? openidDisplayName, }) async { // prepare messenger to avoid "context use across async gaps" final messenger = ScaffoldMessenger.of(navigator.context); @@ -145,6 +198,7 @@ class TunnelService { payload.devicePublicKey, payload.networkId, method, + payload.postureCheckRequired, ); if (method == MfaMethod.openid) { // perform openid-based MFA @@ -153,6 +207,7 @@ class TunnelService { token: startMfaResponse.token, proxyUrl: proxyUrl, method: method, + openidDisplayName: openidDisplayName, ); } if (method == MfaMethod.biometric) { @@ -223,11 +278,16 @@ class TunnelService { required String token, required String proxyUrl, required MfaMethod method, + String? openidDisplayName, }) async { final presharedKey = await Navigator.of(navigator.context).push( MaterialPageRoute( builder: (context) => OpenIdMfaScreen( - screenData: OpenIdMfaScreenData(proxyUrl: proxyUrl, token: token), + screenData: OpenIdMfaScreenData( + proxyUrl: proxyUrl, + token: token, + openidDisplayName: openidDisplayName, + ), ), ), ); @@ -272,20 +332,42 @@ class TunnelService { String pubkey, int networkId, MfaMethod method, + bool postureCheckRequired, ) async { talker.debug( "Starting MFA for networkId: $networkId, method: ${method.toReadableString()}", ); + final postureData = postureCheckRequired ? await getPosture() : null; final request = StartMfaRequest( pubkey: pubkey, locationId: networkId, method: method, + postureData: postureData, ); final uri = Uri.parse(url); return await proxyApi.startMfa(uri, request); } + /// Calls `/posture/connect` endpoint and returns runtime preshared key. + static Future _authorizePostureOnly( + String url, + String pubkey, + int networkId, + String pollingToken, + ) async { + talker.debug('Starting posture check for networkId: $networkId'); + final request = PostureConnectRequest( + locationId: networkId, + pubkey: pubkey, + devicePostureData: await getPosture(), + token: pollingToken, + ); + + final response = await proxyApi.postureConnect(Uri.parse(url), request); + return response.presharedKey; + } + /// Prepares wireguard plugin configuration static PluginConnectPayload _makePayload( DefguardInstance instance, @@ -306,6 +388,7 @@ class TunnelService { networkId: location.networkId, instanceId: instance.id, traffic: trafficMethod, + postureCheckRequired: location.postureCheckRequired == true, ); } diff --git a/client/lib/open/screens/instance/widgets/delete_instance_dialog.dart b/client/lib/open/screens/instance/widgets/delete_instance_dialog.dart index 26976d6..e60477d 100644 --- a/client/lib/open/screens/instance/widgets/delete_instance_dialog.dart +++ b/client/lib/open/screens/instance/widgets/delete_instance_dialog.dart @@ -10,7 +10,6 @@ import 'package:mobile/utils/secure_storage.dart'; import '../../../services/snackbar_service.dart'; - class DeleteInstanceDialog extends HookConsumerWidget { final DefguardInstance instance; @@ -25,7 +24,7 @@ class DeleteInstanceDialog extends HookConsumerWidget { Future deleteInstance(BuildContext context) async { try { - if(instance.mfaKeysStored) { + if (instance.mfaKeysStored) { await removeInstanceStorage(instance.secureStorageKey); } await db.managers.defguardInstances @@ -35,8 +34,10 @@ class DeleteInstanceDialog extends HookConsumerWidget { SnackbarService.show("Instance deleted"); Navigator.of(context).pop(); } - } catch(e) { - talker.error("Failed to delete instance ${instance.logName}! Reason: \n $e"); + } catch (e) { + talker.error( + "Failed to delete instance ${instance.logName}! Reason: \n $e", + ); } } diff --git a/client/lib/open/screens/instance/widgets/routing_method_dialog.dart b/client/lib/open/screens/instance/widgets/routing_method_dialog.dart index d2cc29b..e64736e 100644 --- a/client/lib/open/screens/instance/widgets/routing_method_dialog.dart +++ b/client/lib/open/screens/instance/widgets/routing_method_dialog.dart @@ -24,12 +24,14 @@ enum RoutingMethodDialogIntention { connect, save, next } class RoutingMethodDialog extends HookConsumerWidget { final Location location; + final ClientTrafficPolicy clientTrafficPolicy; final RoutingMethodDialogIntention intention; const RoutingMethodDialog({ super.key, required this.location, required this.intention, + required this.clientTrafficPolicy, }); String _getSubmitText() { diff --git a/client/lib/open/screens/mfa/mfa_code_screen.dart b/client/lib/open/screens/mfa/mfa_code_screen.dart index 1a71e0f..5a01c1a 100644 --- a/client/lib/open/screens/mfa/mfa_code_screen.dart +++ b/client/lib/open/screens/mfa/mfa_code_screen.dart @@ -11,6 +11,7 @@ import 'package:mobile/open/widgets/navigation/dg_scaffold.dart'; import 'package:mobile/theme/color.dart'; import 'package:mobile/theme/spacing.dart'; import 'package:mobile/theme/text.dart'; +import 'package:mobile/utils/error_handler.dart'; import 'package:mobile/utils/screen_padding.dart'; import '../../../../../data/db/enums.dart'; @@ -151,9 +152,15 @@ class _CodeForm extends HookConsumerWidget { if (e.response?.statusCode == 401) { codeInvalid.value = true; formKey.currentState?.validate(); + } else { + SnackbarService.showError( + ErrorHandler.getHumanReadableError(e), + ); } } catch (e) { - SnackbarService.showError("Error: $e"); + SnackbarService.showError( + ErrorHandler.getHumanReadableError(e), + ); } finally { isLoading.value = false; } diff --git a/client/lib/open/widgets/buttons/dg_text_button.dart b/client/lib/open/widgets/buttons/dg_text_button.dart index 72d43ad..297078f 100644 --- a/client/lib/open/widgets/buttons/dg_text_button.dart +++ b/client/lib/open/widgets/buttons/dg_text_button.dart @@ -39,7 +39,10 @@ class DgTextButton extends StatelessWidget { child: Text( text, textAlign: TextAlign.center, - style: textStyle.copyWith(decoration: TextDecoration.underline, decorationColor: textStyle.color), + style: textStyle.copyWith( + decoration: TextDecoration.underline, + decorationColor: textStyle.color, + ), ), ), ), diff --git a/client/lib/open/widgets/dg_checkbox.dart b/client/lib/open/widgets/dg_checkbox.dart index 90a7d5e..a71a950 100644 --- a/client/lib/open/widgets/dg_checkbox.dart +++ b/client/lib/open/widgets/dg_checkbox.dart @@ -31,7 +31,7 @@ class DgCheckbox extends StatelessWidget { Widget _getBody() { final icon = DgIconCheckbox(size: iconSize, variant: _getIconVariant()); TextStyle textStyleInner; - if(textStyle == null) { + if (textStyle == null) { textStyleInner = DgText.modal1.copyWith(color: DgColor.textBodySecondary); } else { textStyleInner = textStyle!; diff --git a/client/lib/open/widgets/dg_menu.dart b/client/lib/open/widgets/dg_menu.dart index 651d95e..f6d3c47 100644 --- a/client/lib/open/widgets/dg_menu.dart +++ b/client/lib/open/widgets/dg_menu.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -31,7 +30,9 @@ class DgMenu extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final topOffset = useMemoized(() => anchorGeometry.position.dy + 10 + anchorGeometry.size.height); + final topOffset = useMemoized( + () => anchorGeometry.position.dy + 10 + anchorGeometry.size.height, + ); final leftOffset = useMemoized(() => anchorGeometry.position.dx); final animationController = useAnimationController( duration: 100.ms, @@ -67,8 +68,11 @@ class DgMenu extends HookConsumerWidget { builder: (context, _) => FadeTransition( opacity: animationController, child: SlideTransition( - position: Tween(begin: Offset(0, -0.05), end: Offset.zero) - .animate( + position: + Tween( + begin: Offset(0, -0.05), + end: Offset.zero, + ).animate( CurvedAnimation( parent: animationController, curve: Curves.easeOut, diff --git a/client/lib/open/widgets/loading_screen.dart b/client/lib/open/widgets/loading_screen.dart index a5e1bc5..4ed189a 100644 --- a/client/lib/open/widgets/loading_screen.dart +++ b/client/lib/open/widgets/loading_screen.dart @@ -29,7 +29,7 @@ class LoadingView extends StatelessWidget { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, children: [ - DgCircularProgress(color: DgColor.iconSecondary, size: 92), + DgCircularProgress(color: DgColor.iconSecondary, size: 92), ], ), ); diff --git a/client/lib/router/routes.dart b/client/lib/router/routes.dart index 3994ccd..c7c4f22 100644 --- a/client/lib/router/routes.dart +++ b/client/lib/router/routes.dart @@ -21,8 +21,7 @@ part 'routes.g.dart'; @TypedGoRoute(path: "/process_qr") @immutable -class ProcessQrScreenRoute extends GoRouteData - with _$ProcessQrScreenRoute { +class ProcessQrScreenRoute extends GoRouteData with _$ProcessQrScreenRoute { const ProcessQrScreenRoute(this.$extra); final ProcessQrScreenData $extra; @@ -35,8 +34,7 @@ class ProcessQrScreenRoute extends GoRouteData @TypedGoRoute(path: '/') @immutable -class HomeScreenRoute extends GoRouteData - with _$HomeScreenRoute { +class HomeScreenRoute extends GoRouteData with _$HomeScreenRoute { const HomeScreenRoute(); @override @@ -47,8 +45,7 @@ class HomeScreenRoute extends GoRouteData @TypedGoRoute(path: "/qr") @immutable -class QRScreenRoute extends GoRouteData - with _$QRScreenRoute { +class QRScreenRoute extends GoRouteData with _$QRScreenRoute { const QRScreenRoute(this.$extra); final QrScreenData $extra; @@ -61,8 +58,7 @@ class QRScreenRoute extends GoRouteData @TypedGoRoute(path: "/instance/:id") @immutable -class InstanceScreenRoute extends GoRouteData - with _$InstanceScreenRoute { +class InstanceScreenRoute extends GoRouteData with _$InstanceScreenRoute { final String id; const InstanceScreenRoute({required this.id}); @@ -75,8 +71,7 @@ class InstanceScreenRoute extends GoRouteData @TypedGoRoute(path: "/add_instance/name_device") @immutable -class NameDeviceScreenRoute extends GoRouteData - with _$NameDeviceScreenRoute { +class NameDeviceScreenRoute extends GoRouteData with _$NameDeviceScreenRoute { const NameDeviceScreenRoute(this.$extra); final NameDeviceScreenData $extra; @@ -99,8 +94,7 @@ class AddInstanceFormScreenRoute extends GoRouteData @TypedGoRoute(path: '/add_instance/init') @immutable -class AddInstanceScreenRoute extends GoRouteData - with _$AddInstanceScreenRoute { +class AddInstanceScreenRoute extends GoRouteData with _$AddInstanceScreenRoute { const AddInstanceScreenRoute(); @override @@ -111,8 +105,7 @@ class AddInstanceScreenRoute extends GoRouteData @TypedGoRoute(path: "/talker") @immutable -class TalkerScreenRoute extends GoRouteData - with _$TalkerScreenRoute { +class TalkerScreenRoute extends GoRouteData with _$TalkerScreenRoute { @override Widget build(BuildContext context, GoRouterState state) { return TalkerScreen(talker: talker); @@ -121,8 +114,7 @@ class TalkerScreenRoute extends GoRouteData @TypedGoRoute(path: "/mfa/openid") @immutable -class OpenIdMfaScreenRoute extends GoRouteData - with _$OpenIdMfaScreenRoute { +class OpenIdMfaScreenRoute extends GoRouteData with _$OpenIdMfaScreenRoute { const OpenIdMfaScreenRoute(this.$extra); final OpenIdMfaScreenData $extra; @@ -149,8 +141,7 @@ class OpenIdMfaWaitingScreenRoute extends GoRouteData @TypedGoRoute(path: "/mfa/code") @immutable -class MfaCodeScreenRoute extends GoRouteData - with _$MfaCodeScreenRoute { +class MfaCodeScreenRoute extends GoRouteData with _$MfaCodeScreenRoute { const MfaCodeScreenRoute(this.$extra); final MfaCodeScreenData $extra; diff --git a/client/lib/theme/color.dart b/client/lib/theme/color.dart index dac138c..b797de5 100644 --- a/client/lib/theme/color.dart +++ b/client/lib/theme/color.dart @@ -81,4 +81,4 @@ final BoxShadow dgBoxShadow = BoxShadow( offset: Offset(0, 12), blurRadius: 24, spreadRadius: 0, -); \ No newline at end of file +); diff --git a/client/lib/theme/text.dart b/client/lib/theme/text.dart index 2e37da8..484c6dd 100644 --- a/client/lib/theme/text.dart +++ b/client/lib/theme/text.dart @@ -25,7 +25,7 @@ class DgText { static const TextStyle body2 = TextStyle( fontFamily: _poppins, fontWeight: FontWeight.w400, - fontSize: 15 + fontSize: 15, ); static const TextStyle modal1 = TextStyle( fontFamily: _roboto, diff --git a/client/lib/utils/error_handler.dart b/client/lib/utils/error_handler.dart new file mode 100644 index 0000000..6738769 --- /dev/null +++ b/client/lib/utils/error_handler.dart @@ -0,0 +1,49 @@ +import 'package:dio/dio.dart'; + +class ErrorHandler { + static String getHumanReadableError(Object e) { + if (e is DioException) { + switch (e.type) { + case DioExceptionType.connectionTimeout: + case DioExceptionType.sendTimeout: + case DioExceptionType.receiveTimeout: + return "Connection timed out. Please check your internet connection."; + case DioExceptionType.connectionError: + return "Unable to connect to the server. Please check your internet connection."; + case DioExceptionType.badResponse: + if (e.response?.statusCode == 401) { + return "Unauthorized. Please check your credentials."; + } + if (e.response?.statusCode == 403) { + return "Access forbidden."; + } + if (e.response?.statusCode == 404) { + return "Service not found."; + } + if (e.response?.statusCode != null && + e.response!.statusCode! >= 500) { + return "Server error. Please try again later."; + } + return "Server returned an error: ${e.response?.statusCode}"; + case DioExceptionType.cancel: + return "Request was cancelled."; + default: + // Handle specific iOS error -1005 (Network connection lost) + final errorString = e.error?.toString() ?? ""; + final messageString = e.message ?? ""; + if (errorString.contains("-1005") || + messageString.contains("-1005")) { + return "Network connection lost. Please try again."; + } + + return "An unexpected network error occurred."; + } + } + + final s = e.toString(); + if (s.startsWith("Exception: ")) { + return s.substring(11); + } + return s; + } +} diff --git a/client/lib/utils/position.dart b/client/lib/utils/position.dart index c09f723..4984d53 100644 --- a/client/lib/utils/position.dart +++ b/client/lib/utils/position.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; Offset? getRenderObjectPosition(RenderObject? renderObject) { - if(renderObject is RenderBox && renderObject.hasSize) { + if (renderObject is RenderBox && renderObject.hasSize) { return renderObject.localToGlobal(Offset.zero); } return null; @@ -11,18 +11,15 @@ class WidgetGeometry { final Offset position; final Size size; - const WidgetGeometry({ - required this.position, - required this.size, - }); + const WidgetGeometry({required this.position, required this.size}); static WidgetGeometry fromKey(GlobalKey key) { final renderObject = key.currentContext?.findRenderObject(); - if(renderObject is RenderBox && renderObject.hasSize) { + if (renderObject is RenderBox && renderObject.hasSize) { final position = renderObject.localToGlobal(Offset.zero); final size = renderObject.size; return WidgetGeometry(position: position, size: size); } return WidgetGeometry(position: Offset.zero, size: Size.zero); } -} \ No newline at end of file +} diff --git a/client/lib/utils/safe_insets.dart b/client/lib/utils/safe_insets.dart index 6bd86a2..cb9b9b7 100644 --- a/client/lib/utils/safe_insets.dart +++ b/client/lib/utils/safe_insets.dart @@ -1,11 +1,13 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; -(double, double) safeInsetHorizontal(BuildContext context, - double preferredPadding) { - final safe = MediaQuery - .of(context) - .padding; - return (math.max(safe.left, preferredPadding), math.max( - safe.right, preferredPadding)); +(double, double) safeInsetHorizontal( + BuildContext context, + double preferredPadding, +) { + final safe = MediaQuery.of(context).padding; + return ( + math.max(safe.left, preferredPadding), + math.max(safe.right, preferredPadding), + ); } diff --git a/client/lib/utils/update_instance.dart b/client/lib/utils/update_instance.dart index 81b079f..5280494 100644 --- a/client/lib/utils/update_instance.dart +++ b/client/lib/utils/update_instance.dart @@ -60,7 +60,8 @@ Future updateInstance({ await db.managers.defguardInstances .filter((row) => row.id.equals(instance.id)) .update( - (_) => DefguardInstancesCompanion(poolingToken: drift.Value(token)), + (_) => + DefguardInstancesCompanion(poolingToken: drift.Value(token)), ); talker.debug("${instance.logName} token updated"); } @@ -124,7 +125,7 @@ String getInstanceUpdateMessage( UpdateInstanceResult updateResult, ) { final buffer = StringBuffer(); - if(updateResult.instanceChanged) { + if (updateResult.instanceChanged) { buffer.write("Instance information updated. "); } if (updateResult.locationsRemoved.isNotEmpty) { diff --git a/client/proto/client_platform_info.proto b/client/proto/client_platform_info.proto new file mode 100644 index 0000000..c90a3ef --- /dev/null +++ b/client/proto/client_platform_info.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package defguard.proxy; + +message ClientPlatformInfo { + string os_family = 1; + string os_type = 2; + string version = 3; + optional string edition = 4; + optional string codename = 5; + optional string bitness = 6; + optional string architecture = 7; +} diff --git a/client/pubspec.lock b/client/pubspec.lock index 3555290..05b6566 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "4.0.9" args: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.13.1" boolean_selector: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" + sha256: fd754058c342243718d5171a95f352cfc9fcf0cba8cfa26df67cb13a5836db78 url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "4.1.2" build_resolvers: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: built_value - sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d + sha256: "31b24be6615ec7fcf70b3aa5a7469fe35826485e639a16dd7eb83ba30e4cc6a8" url: "https://pub.dev" source: hosted - version: "8.12.0" + version: "8.12.7" characters: dependency: transitive description: @@ -233,14 +233,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + url: "https://pub.dev" + source: hosted + version: "1.2.1" code_builder: dependency: transitive description: name: code_builder - sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" url: "https://pub.dev" source: hosted - version: "4.11.0" + version: "4.11.1" collection: dependency: "direct main" description: @@ -261,34 +269,42 @@ packages: dependency: "direct main" description: name: cookie_jar - sha256: a6ac027d3ed6ed756bfce8f3ff60cb479e266f3b0fdabd6242b804b6765e52de + sha256: "963da02c1ef64cb5ac20de948c9e5940aa351f1e34a12b1d327c83d85b7e8fff" url: "https://pub.dev" source: hosted - version: "4.0.8" + version: "4.0.9" coverage: dependency: transitive description: name: coverage - sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.15.1" + cronet_http: + dependency: transitive + description: + name: cronet_http + sha256: "9da9860b409d71e4b8259e3dee631176d499dee23e7cd45a3024ebd5181997d8" + url: "https://pub.dev" + source: hosted + version: "1.9.0" cross_file: dependency: transitive description: name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51" url: "https://pub.dev" source: hosted - version: "0.3.4+2" + version: "0.3.5+4" crypto: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" csslib: dependency: transitive description: @@ -297,14 +313,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + cupertino_http: + dependency: transitive + description: + name: cupertino_http + sha256: "3c8c69cc1b94b9c7570d9454bf1e11fa7010b248547a0760843adc71f0c08fbe" + url: "https://pub.dev" + source: hosted + version: "3.0.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" custom_lint: dependency: "direct dev" description: @@ -349,10 +373,10 @@ packages: dependency: transitive description: name: dbus - sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.14" device_info_plus: dependency: "direct main" description: @@ -373,26 +397,26 @@ packages: dependency: "direct main" description: name: dio - sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 + sha256: "0df44ebba85e503958eb75d07eedd3c86275a58c1d3eda2f2ce8f0a2c3abbb3c" url: "https://pub.dev" source: hosted - version: "5.9.0" + version: "5.11.0" dio_cookie_manager: dependency: "direct main" description: name: dio_cookie_manager - sha256: d39c16abcc711c871b7b29bd51c6b5f3059ef39503916c6a9df7e22c4fc595e0 + sha256: "4ed4669cacb11931517c1158876a2189f19386674b9dab498abcca063dbe4c61" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.5.0" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + sha256: "0786d0b7295a373de356fc0af4f6f1d0ab2844ed31b19dfc5e7556b70e24212c" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.1" drift: dependency: "direct main" description: @@ -437,10 +461,10 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" file: dependency: transitive description: @@ -498,10 +522,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: "7ed76be64e8a7d01dfdf250b8434618e2a028c9dfa2a3c41dc9b531d4b3fc8a5" + sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875" url: "https://pub.dev" source: hosted - version: "19.4.2" + version: "19.5.0" flutter_local_notifications_linux: dependency: transitive description: @@ -530,18 +554,18 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: "8321a6d11a8d13977fa780c89de8d257cce3d841eecfb7a4cadffcc4f12d82dc" + sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: b0694b7fb1689b0e6cc193b3f1fcac6423c4f93c74fb20b806c6b6f196db0c31 + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" url: "https://pub.dev" source: hosted - version: "2.0.30" + version: "2.0.35" flutter_riverpod: dependency: "direct main" description: @@ -610,10 +634,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 + sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" flutter_test: dependency: "direct dev" description: flutter @@ -684,10 +708,18 @@ packages: dependency: transitive description: name: gtk - sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" + hooks: + dependency: transitive + description: + name: hooks + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + url: "https://pub.dev" + source: hosted + version: "2.0.2" hooks_riverpod: dependency: "direct main" description: @@ -700,10 +732,10 @@ packages: dependency: transitive description: name: hotreloader - sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b + sha256: "66871df468fc24eee81f1a0a7cb98acc104716f9b7376d355437b48d633c4ebf" url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.4.0" html: dependency: transitive description: @@ -716,10 +748,10 @@ packages: dependency: "direct main" description: name: http - sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -736,22 +768,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + http_profile: + dependency: transitive + description: + name: http_profile + sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" + url: "https://pub.dev" + source: hosted + version: "0.1.0" image: dependency: transitive description: name: image - sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce url: "https://pub.dev" source: hosted - version: "4.5.4" + version: "4.8.0" intl: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" io: dependency: transitive description: @@ -760,6 +800,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + jni: + dependency: transitive + description: + name: jni + sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "7b717011ea40d04fd47c2731d3d1d36eb99eba3435c2753d62489e8c3c9991d5" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + jni_util: + dependency: transitive + description: + name: jni_util + sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f" + url: "https://pub.dev" + source: hosted + version: "1.0.0" js: dependency: transitive description: @@ -812,10 +876,10 @@ packages: dependency: transitive description: name: lints - sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.0" local_auth: dependency: "direct main" description: @@ -828,26 +892,26 @@ packages: dependency: transitive description: name: local_auth_android - sha256: "48924f4a8b3cc45994ad5993e2e232d3b00788a305c1bf1c7db32cef281ce9a3" + sha256: a0bdfcc0607050a26ef5b31d6b4b254581c3d3ce3c1816ab4d4f4a9173e84467 url: "https://pub.dev" source: hosted - version: "1.0.52" + version: "1.0.56" local_auth_darwin: dependency: transitive description: name: local_auth_darwin - sha256: "0e9706a8543a4a2eee60346294d6a633dd7c3ee60fae6b752570457c4ff32055" + sha256: "699873970067a40ef2f2c09b4c72eb1cfef64224ef041b3df9fdc5c4c1f91f49" url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "1.6.1" local_auth_platform_interface: dependency: transitive description: name: local_auth_platform_interface - sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122 url: "https://pub.dev" source: hosted - version: "1.0.10" + version: "1.1.0" local_auth_windows: dependency: transitive description: @@ -884,10 +948,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -900,10 +964,26 @@ packages: dependency: "direct main" description: name: mobile_scanner - sha256: "5e7e09d904dc01de071b79b3f3789b302b0ed3c9c963109cd3f83ad90de62ecf" + sha256: ce3f059ebd6dbfab7292bba0e893e354b46730636820d3c9ef69005ce2d55bce url: "https://pub.dev" source: hosted - version: "7.1.2" + version: "7.4.0" + native_dio_adapter: + dependency: "direct main" + description: + name: native_dio_adapter + sha256: "7ca3d04c76095a02c3b0d2e6f3c90e7781373671f7aacad34b81f31074a7369b" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + native_toolchain_c: + dependency: transitive + description: + name: native_toolchain_c + sha256: f9c168717100ae6d9fee9ffb0be379bf1f8b26b0f6bcbd4fdddcd931993a6a72 + url: "https://pub.dev" + source: hosted + version: "0.19.2" node_preamble: dependency: transitive description: @@ -912,6 +992,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e + url: "https://pub.dev" + source: hosted + version: "9.5.0" package_config: dependency: transitive description: @@ -956,42 +1044,42 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.6" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db" + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" url: "https://pub.dev" source: hosted - version: "2.2.18" + version: "2.3.1" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd" + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.6.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_windows: dependency: transitive description: @@ -1004,10 +1092,10 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 + sha256: fe54465bcc62a4564c6e4db337bbaded6c0c0fa6e10487414436d163114784f6 url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "12.0.3" permission_handler_android: dependency: transitive description: @@ -1020,42 +1108,42 @@ packages: dependency: transitive description: name: permission_handler_apple - sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + sha256: f49cb15a064ea9d974fc7fbb302099353b7b170d07284e86e264561579e5bcf8 url: "https://pub.dev" source: hosted - version: "9.4.7" + version: "9.6.1" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + sha256: "6ea98b3f17f60d3b527f2647ed2ab4dc0f6bfe25b22cb1c363f5d8f62252f6ac" url: "https://pub.dev" source: hosted - version: "0.1.3+5" + version: "0.1.4+1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + sha256: a5c8a97ecf5616112a5b16d4b8e9ec0e5ae90ef63ac69c0d7b8ae240be760b23 url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.4.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + sha256: caeae01858a0a7d2df67a445ac98e1ad95e55a0e77c73044f4e9b1c8c2289cbd url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.2.2" petitparser: dependency: transitive description: name: petitparser - sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.2" platform: dependency: transitive description: @@ -1084,10 +1172,18 @@ packages: dependency: transitive description: name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.5.2" + protobuf: + dependency: "direct main" + description: + name: protobuf + sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06" + url: "https://pub.dev" + source: hosted + version: "6.0.0" pub_semver: dependency: "direct main" description: @@ -1112,6 +1208,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" riverpod: dependency: transitive description: @@ -1180,26 +1284,26 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 url: "https://pub.dev" source: hosted - version: "2.4.13" + version: "2.4.23" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.6" shared_preferences_linux: dependency: transitive description: @@ -1212,10 +1316,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_web: dependency: transitive description: @@ -1305,26 +1409,18 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" - source: hosted - version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "1.10.2" sqlite3: dependency: transitive description: name: sqlite3 - sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924 + sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.9.4" sqlite3_flutter_libs: dependency: "direct main" description: @@ -1425,26 +1521,26 @@ packages: dependency: transitive description: name: test - sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" url: "https://pub.dev" source: hosted - version: "1.26.2" + version: "1.26.3" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.7" test_core: dependency: transitive description: name: test_core - sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" timezone: dependency: transitive description: @@ -1481,10 +1577,10 @@ packages: dependency: transitive description: name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.1" url_launcher: dependency: "direct main" description: @@ -1497,34 +1593,34 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "81777b08c498a292d93ff2feead633174c386291e35612f8da438d6e92c4447e" + sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c" url: "https://pub.dev" source: hosted - version: "6.3.20" + version: "6.3.30" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" url: "https://pub.dev" source: hosted - version: "6.3.4" + version: "6.4.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.2.5" url_launcher_platform_interface: dependency: transitive description: @@ -1537,34 +1633,34 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" uuid: dependency: "direct main" description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.6.0" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + sha256: "9d0e3b9cb16542ad660daee871e726a10d13a93b7b5391677c3160e8f5e83935" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.2.3" vector_graphics_codec: dependency: transitive description: @@ -1577,10 +1673,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc + sha256: "4dca4feb77dc3ec7f6e27e49c53241eb8217f55e4f9b12599a27f8903bca5682" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.3.0" vector_math: dependency: transitive description: @@ -1593,18 +1689,18 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.2.0" watcher: dependency: transitive description: name: watcher - sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.1" web: dependency: transitive description: @@ -1641,10 +1737,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.15.0" win32_registry: dependency: transitive description: @@ -1693,5 +1789,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.1 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.10.3 <4.0.0" + flutter: ">=3.38.4" diff --git a/client/pubspec.yaml b/client/pubspec.yaml index ad1c57d..cfbb766 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -2,7 +2,7 @@ name: mobile description: "Defguard mobile client" # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.6.0+1 +version: 1.7.0+1 environment: sdk: ^3.8.1 @@ -50,6 +50,7 @@ dependencies: cookie_jar: ^4.0.8 dio: ^5.8.0+1 dio_cookie_manager: ^3.2.0 + native_dio_adapter: ^1.3.0 flutter_native_splash: ^2.4.6 flutter_launcher_icons: ^0.14.4 flutter_svg: ^2.1.0 @@ -76,6 +77,7 @@ dependencies: shared_preferences: ^2.5.3 flutter_secure_storage: ^9.2.4 device_info_plus: ^11.5.0 + protobuf: ^6.0.0 dev_dependencies: flutter_test: @@ -101,34 +103,33 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true assets: - - assets/icons/ + - assets/icons/ fonts: - - family: Roboto - fonts: - - asset: assets/fonts/roboto-400.ttf - weight: 400 - - asset: assets/fonts/roboto-500.ttf - weight: 500 - - asset: assets/fonts/roboto-600.ttf - weight: 600 - - family: Poppins - fonts: - - asset: assets/fonts/poppins-300.ttf - weight: 300 - - asset: assets/fonts/poppins-400.ttf - weight: 400 - - asset: assets/fonts/poppins-500.ttf - weight: 500 - - asset: assets/fonts/poppins-600.ttf - weight: 600 + - family: Roboto + fonts: + - asset: assets/fonts/roboto-400.ttf + weight: 400 + - asset: assets/fonts/roboto-500.ttf + weight: 500 + - asset: assets/fonts/roboto-600.ttf + weight: 600 + - family: Poppins + fonts: + - asset: assets/fonts/poppins-300.ttf + weight: 300 + - asset: assets/fonts/poppins-400.ttf + weight: 400 + - asset: assets/fonts/poppins-500.ttf + weight: 500 + - asset: assets/fonts/poppins-600.ttf + weight: 600 # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg diff --git a/client/test/drift/defguard/generated/schema.dart b/client/test/drift/defguard/generated/schema.dart new file mode 100644 index 0000000..22131b1 --- /dev/null +++ b/client/test/drift/defguard/generated/schema.dart @@ -0,0 +1,29 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; +import 'package:drift/internal/migrations.dart'; +import 'schema_v1.dart' as v1; +import 'schema_v2.dart' as v2; +import 'schema_v3.dart' as v3; +import 'schema_v4.dart' as v4; + +class GeneratedHelper implements SchemaInstantiationHelper { + @override + GeneratedDatabase databaseForVersion(QueryExecutor db, int version) { + switch (version) { + case 1: + return v1.DatabaseAtV1(db); + case 2: + return v2.DatabaseAtV2(db); + case 3: + return v3.DatabaseAtV3(db); + case 4: + return v4.DatabaseAtV4(db); + default: + throw MissingSchemaException(version, versions); + } + } + + static const versions = const [1, 2, 3, 4]; +} diff --git a/client/test/drift/defguard/generated/schema_v1.dart b/client/test/drift/defguard/generated/schema_v1.dart new file mode 100644 index 0000000..99d607b --- /dev/null +++ b/client/test/drift/defguard/generated/schema_v1.dart @@ -0,0 +1,1275 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class DefguardInstances extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + DefguardInstances(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn uuid = GeneratedColumn( + 'uuid', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn url = GeneratedColumn( + 'url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn deviceId = GeneratedColumn( + 'device_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn proxyUrl = GeneratedColumn( + 'proxy_url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn poolingToken = GeneratedColumn( + 'pooling_token', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn disableAllTraffic = GeneratedColumn( + 'disable_all_traffic', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("disable_all_traffic" IN (0, 1))', + ), + ); + late final GeneratedColumn enterpriseEnabled = GeneratedColumn( + 'enterprise_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("enterprise_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn privateKey = GeneratedColumn( + 'private_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn mfaKeysStored = GeneratedColumn( + 'mfa_keys_stored', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_keys_stored" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + disableAllTraffic, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'defguard_instances'; + @override + Set get $primaryKey => {id}; + @override + DefguardInstancesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return DefguardInstancesData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + uuid: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}uuid'], + )!, + url: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}url'], + )!, + deviceId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}device_id'], + )!, + proxyUrl: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}proxy_url'], + )!, + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + )!, + poolingToken: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pooling_token'], + )!, + disableAllTraffic: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}disable_all_traffic'], + )!, + enterpriseEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}enterprise_enabled'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + privateKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}private_key'], + )!, + mfaKeysStored: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_keys_stored'], + )!, + ); + } + + @override + DefguardInstances createAlias(String alias) { + return DefguardInstances(attachedDatabase, alias); + } +} + +class DefguardInstancesData extends DataClass + implements Insertable { + final int id; + final String name; + final String uuid; + final String url; + final int deviceId; + final String proxyUrl; + final String username; + final String poolingToken; + final bool disableAllTraffic; + final bool enterpriseEnabled; + final String pubKey; + final String privateKey; + final bool mfaKeysStored; + const DefguardInstancesData({ + required this.id, + required this.name, + required this.uuid, + required this.url, + required this.deviceId, + required this.proxyUrl, + required this.username, + required this.poolingToken, + required this.disableAllTraffic, + required this.enterpriseEnabled, + required this.pubKey, + required this.privateKey, + required this.mfaKeysStored, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['uuid'] = Variable(uuid); + map['url'] = Variable(url); + map['device_id'] = Variable(deviceId); + map['proxy_url'] = Variable(proxyUrl); + map['username'] = Variable(username); + map['pooling_token'] = Variable(poolingToken); + map['disable_all_traffic'] = Variable(disableAllTraffic); + map['enterprise_enabled'] = Variable(enterpriseEnabled); + map['pub_key'] = Variable(pubKey); + map['private_key'] = Variable(privateKey); + map['mfa_keys_stored'] = Variable(mfaKeysStored); + return map; + } + + DefguardInstancesCompanion toCompanion(bool nullToAbsent) { + return DefguardInstancesCompanion( + id: Value(id), + name: Value(name), + uuid: Value(uuid), + url: Value(url), + deviceId: Value(deviceId), + proxyUrl: Value(proxyUrl), + username: Value(username), + poolingToken: Value(poolingToken), + disableAllTraffic: Value(disableAllTraffic), + enterpriseEnabled: Value(enterpriseEnabled), + pubKey: Value(pubKey), + privateKey: Value(privateKey), + mfaKeysStored: Value(mfaKeysStored), + ); + } + + factory DefguardInstancesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return DefguardInstancesData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + uuid: serializer.fromJson(json['uuid']), + url: serializer.fromJson(json['url']), + deviceId: serializer.fromJson(json['deviceId']), + proxyUrl: serializer.fromJson(json['proxyUrl']), + username: serializer.fromJson(json['username']), + poolingToken: serializer.fromJson(json['poolingToken']), + disableAllTraffic: serializer.fromJson(json['disableAllTraffic']), + enterpriseEnabled: serializer.fromJson(json['enterpriseEnabled']), + pubKey: serializer.fromJson(json['pubKey']), + privateKey: serializer.fromJson(json['privateKey']), + mfaKeysStored: serializer.fromJson(json['mfaKeysStored']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'uuid': serializer.toJson(uuid), + 'url': serializer.toJson(url), + 'deviceId': serializer.toJson(deviceId), + 'proxyUrl': serializer.toJson(proxyUrl), + 'username': serializer.toJson(username), + 'poolingToken': serializer.toJson(poolingToken), + 'disableAllTraffic': serializer.toJson(disableAllTraffic), + 'enterpriseEnabled': serializer.toJson(enterpriseEnabled), + 'pubKey': serializer.toJson(pubKey), + 'privateKey': serializer.toJson(privateKey), + 'mfaKeysStored': serializer.toJson(mfaKeysStored), + }; + } + + DefguardInstancesData copyWith({ + int? id, + String? name, + String? uuid, + String? url, + int? deviceId, + String? proxyUrl, + String? username, + String? poolingToken, + bool? disableAllTraffic, + bool? enterpriseEnabled, + String? pubKey, + String? privateKey, + bool? mfaKeysStored, + }) => DefguardInstancesData( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + disableAllTraffic: disableAllTraffic ?? this.disableAllTraffic, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + ); + DefguardInstancesData copyWithCompanion(DefguardInstancesCompanion data) { + return DefguardInstancesData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + uuid: data.uuid.present ? data.uuid.value : this.uuid, + url: data.url.present ? data.url.value : this.url, + deviceId: data.deviceId.present ? data.deviceId.value : this.deviceId, + proxyUrl: data.proxyUrl.present ? data.proxyUrl.value : this.proxyUrl, + username: data.username.present ? data.username.value : this.username, + poolingToken: data.poolingToken.present + ? data.poolingToken.value + : this.poolingToken, + disableAllTraffic: data.disableAllTraffic.present + ? data.disableAllTraffic.value + : this.disableAllTraffic, + enterpriseEnabled: data.enterpriseEnabled.present + ? data.enterpriseEnabled.value + : this.enterpriseEnabled, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + privateKey: data.privateKey.present + ? data.privateKey.value + : this.privateKey, + mfaKeysStored: data.mfaKeysStored.present + ? data.mfaKeysStored.value + : this.mfaKeysStored, + ); + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('disableAllTraffic: $disableAllTraffic, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + disableAllTraffic, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is DefguardInstancesData && + other.id == this.id && + other.name == this.name && + other.uuid == this.uuid && + other.url == this.url && + other.deviceId == this.deviceId && + other.proxyUrl == this.proxyUrl && + other.username == this.username && + other.poolingToken == this.poolingToken && + other.disableAllTraffic == this.disableAllTraffic && + other.enterpriseEnabled == this.enterpriseEnabled && + other.pubKey == this.pubKey && + other.privateKey == this.privateKey && + other.mfaKeysStored == this.mfaKeysStored); +} + +class DefguardInstancesCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value uuid; + final Value url; + final Value deviceId; + final Value proxyUrl; + final Value username; + final Value poolingToken; + final Value disableAllTraffic; + final Value enterpriseEnabled; + final Value pubKey; + final Value privateKey; + final Value mfaKeysStored; + const DefguardInstancesCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.uuid = const Value.absent(), + this.url = const Value.absent(), + this.deviceId = const Value.absent(), + this.proxyUrl = const Value.absent(), + this.username = const Value.absent(), + this.poolingToken = const Value.absent(), + this.disableAllTraffic = const Value.absent(), + this.enterpriseEnabled = const Value.absent(), + this.pubKey = const Value.absent(), + this.privateKey = const Value.absent(), + this.mfaKeysStored = const Value.absent(), + }); + DefguardInstancesCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String uuid, + required String url, + required int deviceId, + required String proxyUrl, + required String username, + required String poolingToken, + required bool disableAllTraffic, + required bool enterpriseEnabled, + required String pubKey, + required String privateKey, + required bool mfaKeysStored, + }) : name = Value(name), + uuid = Value(uuid), + url = Value(url), + deviceId = Value(deviceId), + proxyUrl = Value(proxyUrl), + username = Value(username), + poolingToken = Value(poolingToken), + disableAllTraffic = Value(disableAllTraffic), + enterpriseEnabled = Value(enterpriseEnabled), + pubKey = Value(pubKey), + privateKey = Value(privateKey), + mfaKeysStored = Value(mfaKeysStored); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? uuid, + Expression? url, + Expression? deviceId, + Expression? proxyUrl, + Expression? username, + Expression? poolingToken, + Expression? disableAllTraffic, + Expression? enterpriseEnabled, + Expression? pubKey, + Expression? privateKey, + Expression? mfaKeysStored, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (uuid != null) 'uuid': uuid, + if (url != null) 'url': url, + if (deviceId != null) 'device_id': deviceId, + if (proxyUrl != null) 'proxy_url': proxyUrl, + if (username != null) 'username': username, + if (poolingToken != null) 'pooling_token': poolingToken, + if (disableAllTraffic != null) 'disable_all_traffic': disableAllTraffic, + if (enterpriseEnabled != null) 'enterprise_enabled': enterpriseEnabled, + if (pubKey != null) 'pub_key': pubKey, + if (privateKey != null) 'private_key': privateKey, + if (mfaKeysStored != null) 'mfa_keys_stored': mfaKeysStored, + }); + } + + DefguardInstancesCompanion copyWith({ + Value? id, + Value? name, + Value? uuid, + Value? url, + Value? deviceId, + Value? proxyUrl, + Value? username, + Value? poolingToken, + Value? disableAllTraffic, + Value? enterpriseEnabled, + Value? pubKey, + Value? privateKey, + Value? mfaKeysStored, + }) { + return DefguardInstancesCompanion( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + disableAllTraffic: disableAllTraffic ?? this.disableAllTraffic, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (uuid.present) { + map['uuid'] = Variable(uuid.value); + } + if (url.present) { + map['url'] = Variable(url.value); + } + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (proxyUrl.present) { + map['proxy_url'] = Variable(proxyUrl.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (poolingToken.present) { + map['pooling_token'] = Variable(poolingToken.value); + } + if (disableAllTraffic.present) { + map['disable_all_traffic'] = Variable(disableAllTraffic.value); + } + if (enterpriseEnabled.present) { + map['enterprise_enabled'] = Variable(enterpriseEnabled.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (privateKey.present) { + map['private_key'] = Variable(privateKey.value); + } + if (mfaKeysStored.present) { + map['mfa_keys_stored'] = Variable(mfaKeysStored.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('disableAllTraffic: $disableAllTraffic, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored') + ..write(')')) + .toString(); + } +} + +class Locations extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Locations(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn instance = GeneratedColumn( + 'instance', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES defguard_instances (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn networkId = GeneratedColumn( + 'network_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn address = GeneratedColumn( + 'address', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn endpoint = GeneratedColumn( + 'endpoint', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn allowedIps = GeneratedColumn( + 'allowed_ips', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn dns = GeneratedColumn( + 'dns', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaEnabled = GeneratedColumn( + 'mfa_enabled', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn trafficMethod = GeneratedColumn( + 'traffic_method', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaMethod = GeneratedColumn( + 'mfa_method', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn keepAliveInterval = GeneratedColumn( + 'keep_alive_interval', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn locationMfaMode = GeneratedColumn( + 'location_mfa_mode', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'locations'; + @override + Set get $primaryKey => {id}; + @override + LocationsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocationsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + instance: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}instance'], + )!, + networkId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}network_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + address: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}address'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + endpoint: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}endpoint'], + )!, + allowedIps: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}allowed_ips'], + )!, + dns: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}dns'], + ), + mfaEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_enabled'], + ), + trafficMethod: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}traffic_method'], + ), + mfaMethod: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mfa_method'], + ), + keepAliveInterval: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}keep_alive_interval'], + )!, + locationMfaMode: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}location_mfa_mode'], + ), + ); + } + + @override + Locations createAlias(String alias) { + return Locations(attachedDatabase, alias); + } +} + +class LocationsData extends DataClass implements Insertable { + final int id; + final int instance; + final int networkId; + final String name; + final String address; + final String pubKey; + final String endpoint; + final String allowedIps; + final String? dns; + final bool? mfaEnabled; + final String? trafficMethod; + final int? mfaMethod; + final int keepAliveInterval; + final int? locationMfaMode; + const LocationsData({ + required this.id, + required this.instance, + required this.networkId, + required this.name, + required this.address, + required this.pubKey, + required this.endpoint, + required this.allowedIps, + this.dns, + this.mfaEnabled, + this.trafficMethod, + this.mfaMethod, + required this.keepAliveInterval, + this.locationMfaMode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['instance'] = Variable(instance); + map['network_id'] = Variable(networkId); + map['name'] = Variable(name); + map['address'] = Variable(address); + map['pub_key'] = Variable(pubKey); + map['endpoint'] = Variable(endpoint); + map['allowed_ips'] = Variable(allowedIps); + if (!nullToAbsent || dns != null) { + map['dns'] = Variable(dns); + } + if (!nullToAbsent || mfaEnabled != null) { + map['mfa_enabled'] = Variable(mfaEnabled); + } + if (!nullToAbsent || trafficMethod != null) { + map['traffic_method'] = Variable(trafficMethod); + } + if (!nullToAbsent || mfaMethod != null) { + map['mfa_method'] = Variable(mfaMethod); + } + map['keep_alive_interval'] = Variable(keepAliveInterval); + if (!nullToAbsent || locationMfaMode != null) { + map['location_mfa_mode'] = Variable(locationMfaMode); + } + return map; + } + + LocationsCompanion toCompanion(bool nullToAbsent) { + return LocationsCompanion( + id: Value(id), + instance: Value(instance), + networkId: Value(networkId), + name: Value(name), + address: Value(address), + pubKey: Value(pubKey), + endpoint: Value(endpoint), + allowedIps: Value(allowedIps), + dns: dns == null && nullToAbsent ? const Value.absent() : Value(dns), + mfaEnabled: mfaEnabled == null && nullToAbsent + ? const Value.absent() + : Value(mfaEnabled), + trafficMethod: trafficMethod == null && nullToAbsent + ? const Value.absent() + : Value(trafficMethod), + mfaMethod: mfaMethod == null && nullToAbsent + ? const Value.absent() + : Value(mfaMethod), + keepAliveInterval: Value(keepAliveInterval), + locationMfaMode: locationMfaMode == null && nullToAbsent + ? const Value.absent() + : Value(locationMfaMode), + ); + } + + factory LocationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocationsData( + id: serializer.fromJson(json['id']), + instance: serializer.fromJson(json['instance']), + networkId: serializer.fromJson(json['networkId']), + name: serializer.fromJson(json['name']), + address: serializer.fromJson(json['address']), + pubKey: serializer.fromJson(json['pubKey']), + endpoint: serializer.fromJson(json['endpoint']), + allowedIps: serializer.fromJson(json['allowedIps']), + dns: serializer.fromJson(json['dns']), + mfaEnabled: serializer.fromJson(json['mfaEnabled']), + trafficMethod: serializer.fromJson(json['trafficMethod']), + mfaMethod: serializer.fromJson(json['mfaMethod']), + keepAliveInterval: serializer.fromJson(json['keepAliveInterval']), + locationMfaMode: serializer.fromJson(json['locationMfaMode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'instance': serializer.toJson(instance), + 'networkId': serializer.toJson(networkId), + 'name': serializer.toJson(name), + 'address': serializer.toJson(address), + 'pubKey': serializer.toJson(pubKey), + 'endpoint': serializer.toJson(endpoint), + 'allowedIps': serializer.toJson(allowedIps), + 'dns': serializer.toJson(dns), + 'mfaEnabled': serializer.toJson(mfaEnabled), + 'trafficMethod': serializer.toJson(trafficMethod), + 'mfaMethod': serializer.toJson(mfaMethod), + 'keepAliveInterval': serializer.toJson(keepAliveInterval), + 'locationMfaMode': serializer.toJson(locationMfaMode), + }; + } + + LocationsData copyWith({ + int? id, + int? instance, + int? networkId, + String? name, + String? address, + String? pubKey, + String? endpoint, + String? allowedIps, + Value dns = const Value.absent(), + Value mfaEnabled = const Value.absent(), + Value trafficMethod = const Value.absent(), + Value mfaMethod = const Value.absent(), + int? keepAliveInterval, + Value locationMfaMode = const Value.absent(), + }) => LocationsData( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns.present ? dns.value : this.dns, + mfaEnabled: mfaEnabled.present ? mfaEnabled.value : this.mfaEnabled, + trafficMethod: trafficMethod.present + ? trafficMethod.value + : this.trafficMethod, + mfaMethod: mfaMethod.present ? mfaMethod.value : this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode.present + ? locationMfaMode.value + : this.locationMfaMode, + ); + LocationsData copyWithCompanion(LocationsCompanion data) { + return LocationsData( + id: data.id.present ? data.id.value : this.id, + instance: data.instance.present ? data.instance.value : this.instance, + networkId: data.networkId.present ? data.networkId.value : this.networkId, + name: data.name.present ? data.name.value : this.name, + address: data.address.present ? data.address.value : this.address, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + endpoint: data.endpoint.present ? data.endpoint.value : this.endpoint, + allowedIps: data.allowedIps.present + ? data.allowedIps.value + : this.allowedIps, + dns: data.dns.present ? data.dns.value : this.dns, + mfaEnabled: data.mfaEnabled.present + ? data.mfaEnabled.value + : this.mfaEnabled, + trafficMethod: data.trafficMethod.present + ? data.trafficMethod.value + : this.trafficMethod, + mfaMethod: data.mfaMethod.present ? data.mfaMethod.value : this.mfaMethod, + keepAliveInterval: data.keepAliveInterval.present + ? data.keepAliveInterval.value + : this.keepAliveInterval, + locationMfaMode: data.locationMfaMode.present + ? data.locationMfaMode.value + : this.locationMfaMode, + ); + } + + @override + String toString() { + return (StringBuffer('LocationsData(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocationsData && + other.id == this.id && + other.instance == this.instance && + other.networkId == this.networkId && + other.name == this.name && + other.address == this.address && + other.pubKey == this.pubKey && + other.endpoint == this.endpoint && + other.allowedIps == this.allowedIps && + other.dns == this.dns && + other.mfaEnabled == this.mfaEnabled && + other.trafficMethod == this.trafficMethod && + other.mfaMethod == this.mfaMethod && + other.keepAliveInterval == this.keepAliveInterval && + other.locationMfaMode == this.locationMfaMode); +} + +class LocationsCompanion extends UpdateCompanion { + final Value id; + final Value instance; + final Value networkId; + final Value name; + final Value address; + final Value pubKey; + final Value endpoint; + final Value allowedIps; + final Value dns; + final Value mfaEnabled; + final Value trafficMethod; + final Value mfaMethod; + final Value keepAliveInterval; + final Value locationMfaMode; + const LocationsCompanion({ + this.id = const Value.absent(), + this.instance = const Value.absent(), + this.networkId = const Value.absent(), + this.name = const Value.absent(), + this.address = const Value.absent(), + this.pubKey = const Value.absent(), + this.endpoint = const Value.absent(), + this.allowedIps = const Value.absent(), + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + this.keepAliveInterval = const Value.absent(), + this.locationMfaMode = const Value.absent(), + }); + LocationsCompanion.insert({ + this.id = const Value.absent(), + required int instance, + required int networkId, + required String name, + required String address, + required String pubKey, + required String endpoint, + required String allowedIps, + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + required int keepAliveInterval, + this.locationMfaMode = const Value.absent(), + }) : instance = Value(instance), + networkId = Value(networkId), + name = Value(name), + address = Value(address), + pubKey = Value(pubKey), + endpoint = Value(endpoint), + allowedIps = Value(allowedIps), + keepAliveInterval = Value(keepAliveInterval); + static Insertable custom({ + Expression? id, + Expression? instance, + Expression? networkId, + Expression? name, + Expression? address, + Expression? pubKey, + Expression? endpoint, + Expression? allowedIps, + Expression? dns, + Expression? mfaEnabled, + Expression? trafficMethod, + Expression? mfaMethod, + Expression? keepAliveInterval, + Expression? locationMfaMode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (instance != null) 'instance': instance, + if (networkId != null) 'network_id': networkId, + if (name != null) 'name': name, + if (address != null) 'address': address, + if (pubKey != null) 'pub_key': pubKey, + if (endpoint != null) 'endpoint': endpoint, + if (allowedIps != null) 'allowed_ips': allowedIps, + if (dns != null) 'dns': dns, + if (mfaEnabled != null) 'mfa_enabled': mfaEnabled, + if (trafficMethod != null) 'traffic_method': trafficMethod, + if (mfaMethod != null) 'mfa_method': mfaMethod, + if (keepAliveInterval != null) 'keep_alive_interval': keepAliveInterval, + if (locationMfaMode != null) 'location_mfa_mode': locationMfaMode, + }); + } + + LocationsCompanion copyWith({ + Value? id, + Value? instance, + Value? networkId, + Value? name, + Value? address, + Value? pubKey, + Value? endpoint, + Value? allowedIps, + Value? dns, + Value? mfaEnabled, + Value? trafficMethod, + Value? mfaMethod, + Value? keepAliveInterval, + Value? locationMfaMode, + }) { + return LocationsCompanion( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns ?? this.dns, + mfaEnabled: mfaEnabled ?? this.mfaEnabled, + trafficMethod: trafficMethod ?? this.trafficMethod, + mfaMethod: mfaMethod ?? this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode ?? this.locationMfaMode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (instance.present) { + map['instance'] = Variable(instance.value); + } + if (networkId.present) { + map['network_id'] = Variable(networkId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (address.present) { + map['address'] = Variable(address.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (endpoint.present) { + map['endpoint'] = Variable(endpoint.value); + } + if (allowedIps.present) { + map['allowed_ips'] = Variable(allowedIps.value); + } + if (dns.present) { + map['dns'] = Variable(dns.value); + } + if (mfaEnabled.present) { + map['mfa_enabled'] = Variable(mfaEnabled.value); + } + if (trafficMethod.present) { + map['traffic_method'] = Variable(trafficMethod.value); + } + if (mfaMethod.present) { + map['mfa_method'] = Variable(mfaMethod.value); + } + if (keepAliveInterval.present) { + map['keep_alive_interval'] = Variable(keepAliveInterval.value); + } + if (locationMfaMode.present) { + map['location_mfa_mode'] = Variable(locationMfaMode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocationsCompanion(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV1 extends GeneratedDatabase { + DatabaseAtV1(QueryExecutor e) : super(e); + late final DefguardInstances defguardInstances = DefguardInstances(this); + late final Locations locations = Locations(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + defguardInstances, + locations, + ]; + @override + int get schemaVersion => 1; +} diff --git a/client/test/drift/defguard/generated/schema_v2.dart b/client/test/drift/defguard/generated/schema_v2.dart new file mode 100644 index 0000000..7419234 --- /dev/null +++ b/client/test/drift/defguard/generated/schema_v2.dart @@ -0,0 +1,1275 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class DefguardInstances extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + DefguardInstances(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn uuid = GeneratedColumn( + 'uuid', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn url = GeneratedColumn( + 'url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn deviceId = GeneratedColumn( + 'device_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn proxyUrl = GeneratedColumn( + 'proxy_url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn poolingToken = GeneratedColumn( + 'pooling_token', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn clientTrafficPolicy = GeneratedColumn( + 'client_traffic_policy', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn enterpriseEnabled = GeneratedColumn( + 'enterprise_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("enterprise_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn privateKey = GeneratedColumn( + 'private_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn mfaKeysStored = GeneratedColumn( + 'mfa_keys_stored', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_keys_stored" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + clientTrafficPolicy, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'defguard_instances'; + @override + Set get $primaryKey => {id}; + @override + DefguardInstancesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return DefguardInstancesData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + uuid: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}uuid'], + )!, + url: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}url'], + )!, + deviceId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}device_id'], + )!, + proxyUrl: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}proxy_url'], + )!, + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + )!, + poolingToken: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pooling_token'], + )!, + clientTrafficPolicy: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}client_traffic_policy'], + )!, + enterpriseEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}enterprise_enabled'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + privateKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}private_key'], + )!, + mfaKeysStored: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_keys_stored'], + )!, + ); + } + + @override + DefguardInstances createAlias(String alias) { + return DefguardInstances(attachedDatabase, alias); + } +} + +class DefguardInstancesData extends DataClass + implements Insertable { + final int id; + final String name; + final String uuid; + final String url; + final int deviceId; + final String proxyUrl; + final String username; + final String poolingToken; + final int clientTrafficPolicy; + final bool enterpriseEnabled; + final String pubKey; + final String privateKey; + final bool mfaKeysStored; + const DefguardInstancesData({ + required this.id, + required this.name, + required this.uuid, + required this.url, + required this.deviceId, + required this.proxyUrl, + required this.username, + required this.poolingToken, + required this.clientTrafficPolicy, + required this.enterpriseEnabled, + required this.pubKey, + required this.privateKey, + required this.mfaKeysStored, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['uuid'] = Variable(uuid); + map['url'] = Variable(url); + map['device_id'] = Variable(deviceId); + map['proxy_url'] = Variable(proxyUrl); + map['username'] = Variable(username); + map['pooling_token'] = Variable(poolingToken); + map['client_traffic_policy'] = Variable(clientTrafficPolicy); + map['enterprise_enabled'] = Variable(enterpriseEnabled); + map['pub_key'] = Variable(pubKey); + map['private_key'] = Variable(privateKey); + map['mfa_keys_stored'] = Variable(mfaKeysStored); + return map; + } + + DefguardInstancesCompanion toCompanion(bool nullToAbsent) { + return DefguardInstancesCompanion( + id: Value(id), + name: Value(name), + uuid: Value(uuid), + url: Value(url), + deviceId: Value(deviceId), + proxyUrl: Value(proxyUrl), + username: Value(username), + poolingToken: Value(poolingToken), + clientTrafficPolicy: Value(clientTrafficPolicy), + enterpriseEnabled: Value(enterpriseEnabled), + pubKey: Value(pubKey), + privateKey: Value(privateKey), + mfaKeysStored: Value(mfaKeysStored), + ); + } + + factory DefguardInstancesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return DefguardInstancesData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + uuid: serializer.fromJson(json['uuid']), + url: serializer.fromJson(json['url']), + deviceId: serializer.fromJson(json['deviceId']), + proxyUrl: serializer.fromJson(json['proxyUrl']), + username: serializer.fromJson(json['username']), + poolingToken: serializer.fromJson(json['poolingToken']), + clientTrafficPolicy: serializer.fromJson( + json['clientTrafficPolicy'], + ), + enterpriseEnabled: serializer.fromJson(json['enterpriseEnabled']), + pubKey: serializer.fromJson(json['pubKey']), + privateKey: serializer.fromJson(json['privateKey']), + mfaKeysStored: serializer.fromJson(json['mfaKeysStored']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'uuid': serializer.toJson(uuid), + 'url': serializer.toJson(url), + 'deviceId': serializer.toJson(deviceId), + 'proxyUrl': serializer.toJson(proxyUrl), + 'username': serializer.toJson(username), + 'poolingToken': serializer.toJson(poolingToken), + 'clientTrafficPolicy': serializer.toJson(clientTrafficPolicy), + 'enterpriseEnabled': serializer.toJson(enterpriseEnabled), + 'pubKey': serializer.toJson(pubKey), + 'privateKey': serializer.toJson(privateKey), + 'mfaKeysStored': serializer.toJson(mfaKeysStored), + }; + } + + DefguardInstancesData copyWith({ + int? id, + String? name, + String? uuid, + String? url, + int? deviceId, + String? proxyUrl, + String? username, + String? poolingToken, + int? clientTrafficPolicy, + bool? enterpriseEnabled, + String? pubKey, + String? privateKey, + bool? mfaKeysStored, + }) => DefguardInstancesData( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + ); + DefguardInstancesData copyWithCompanion(DefguardInstancesCompanion data) { + return DefguardInstancesData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + uuid: data.uuid.present ? data.uuid.value : this.uuid, + url: data.url.present ? data.url.value : this.url, + deviceId: data.deviceId.present ? data.deviceId.value : this.deviceId, + proxyUrl: data.proxyUrl.present ? data.proxyUrl.value : this.proxyUrl, + username: data.username.present ? data.username.value : this.username, + poolingToken: data.poolingToken.present + ? data.poolingToken.value + : this.poolingToken, + clientTrafficPolicy: data.clientTrafficPolicy.present + ? data.clientTrafficPolicy.value + : this.clientTrafficPolicy, + enterpriseEnabled: data.enterpriseEnabled.present + ? data.enterpriseEnabled.value + : this.enterpriseEnabled, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + privateKey: data.privateKey.present + ? data.privateKey.value + : this.privateKey, + mfaKeysStored: data.mfaKeysStored.present + ? data.mfaKeysStored.value + : this.mfaKeysStored, + ); + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + clientTrafficPolicy, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is DefguardInstancesData && + other.id == this.id && + other.name == this.name && + other.uuid == this.uuid && + other.url == this.url && + other.deviceId == this.deviceId && + other.proxyUrl == this.proxyUrl && + other.username == this.username && + other.poolingToken == this.poolingToken && + other.clientTrafficPolicy == this.clientTrafficPolicy && + other.enterpriseEnabled == this.enterpriseEnabled && + other.pubKey == this.pubKey && + other.privateKey == this.privateKey && + other.mfaKeysStored == this.mfaKeysStored); +} + +class DefguardInstancesCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value uuid; + final Value url; + final Value deviceId; + final Value proxyUrl; + final Value username; + final Value poolingToken; + final Value clientTrafficPolicy; + final Value enterpriseEnabled; + final Value pubKey; + final Value privateKey; + final Value mfaKeysStored; + const DefguardInstancesCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.uuid = const Value.absent(), + this.url = const Value.absent(), + this.deviceId = const Value.absent(), + this.proxyUrl = const Value.absent(), + this.username = const Value.absent(), + this.poolingToken = const Value.absent(), + this.clientTrafficPolicy = const Value.absent(), + this.enterpriseEnabled = const Value.absent(), + this.pubKey = const Value.absent(), + this.privateKey = const Value.absent(), + this.mfaKeysStored = const Value.absent(), + }); + DefguardInstancesCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String uuid, + required String url, + required int deviceId, + required String proxyUrl, + required String username, + required String poolingToken, + this.clientTrafficPolicy = const Value.absent(), + required bool enterpriseEnabled, + required String pubKey, + required String privateKey, + required bool mfaKeysStored, + }) : name = Value(name), + uuid = Value(uuid), + url = Value(url), + deviceId = Value(deviceId), + proxyUrl = Value(proxyUrl), + username = Value(username), + poolingToken = Value(poolingToken), + enterpriseEnabled = Value(enterpriseEnabled), + pubKey = Value(pubKey), + privateKey = Value(privateKey), + mfaKeysStored = Value(mfaKeysStored); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? uuid, + Expression? url, + Expression? deviceId, + Expression? proxyUrl, + Expression? username, + Expression? poolingToken, + Expression? clientTrafficPolicy, + Expression? enterpriseEnabled, + Expression? pubKey, + Expression? privateKey, + Expression? mfaKeysStored, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (uuid != null) 'uuid': uuid, + if (url != null) 'url': url, + if (deviceId != null) 'device_id': deviceId, + if (proxyUrl != null) 'proxy_url': proxyUrl, + if (username != null) 'username': username, + if (poolingToken != null) 'pooling_token': poolingToken, + if (clientTrafficPolicy != null) + 'client_traffic_policy': clientTrafficPolicy, + if (enterpriseEnabled != null) 'enterprise_enabled': enterpriseEnabled, + if (pubKey != null) 'pub_key': pubKey, + if (privateKey != null) 'private_key': privateKey, + if (mfaKeysStored != null) 'mfa_keys_stored': mfaKeysStored, + }); + } + + DefguardInstancesCompanion copyWith({ + Value? id, + Value? name, + Value? uuid, + Value? url, + Value? deviceId, + Value? proxyUrl, + Value? username, + Value? poolingToken, + Value? clientTrafficPolicy, + Value? enterpriseEnabled, + Value? pubKey, + Value? privateKey, + Value? mfaKeysStored, + }) { + return DefguardInstancesCompanion( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (uuid.present) { + map['uuid'] = Variable(uuid.value); + } + if (url.present) { + map['url'] = Variable(url.value); + } + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (proxyUrl.present) { + map['proxy_url'] = Variable(proxyUrl.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (poolingToken.present) { + map['pooling_token'] = Variable(poolingToken.value); + } + if (clientTrafficPolicy.present) { + map['client_traffic_policy'] = Variable(clientTrafficPolicy.value); + } + if (enterpriseEnabled.present) { + map['enterprise_enabled'] = Variable(enterpriseEnabled.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (privateKey.present) { + map['private_key'] = Variable(privateKey.value); + } + if (mfaKeysStored.present) { + map['mfa_keys_stored'] = Variable(mfaKeysStored.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored') + ..write(')')) + .toString(); + } +} + +class Locations extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Locations(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn instance = GeneratedColumn( + 'instance', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES defguard_instances (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn networkId = GeneratedColumn( + 'network_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn address = GeneratedColumn( + 'address', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn endpoint = GeneratedColumn( + 'endpoint', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn allowedIps = GeneratedColumn( + 'allowed_ips', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn dns = GeneratedColumn( + 'dns', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaEnabled = GeneratedColumn( + 'mfa_enabled', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn trafficMethod = GeneratedColumn( + 'traffic_method', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaMethod = GeneratedColumn( + 'mfa_method', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn keepAliveInterval = GeneratedColumn( + 'keep_alive_interval', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn locationMfaMode = GeneratedColumn( + 'location_mfa_mode', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'locations'; + @override + Set get $primaryKey => {id}; + @override + LocationsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocationsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + instance: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}instance'], + )!, + networkId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}network_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + address: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}address'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + endpoint: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}endpoint'], + )!, + allowedIps: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}allowed_ips'], + )!, + dns: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}dns'], + ), + mfaEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_enabled'], + ), + trafficMethod: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}traffic_method'], + ), + mfaMethod: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mfa_method'], + ), + keepAliveInterval: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}keep_alive_interval'], + )!, + locationMfaMode: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}location_mfa_mode'], + ), + ); + } + + @override + Locations createAlias(String alias) { + return Locations(attachedDatabase, alias); + } +} + +class LocationsData extends DataClass implements Insertable { + final int id; + final int instance; + final int networkId; + final String name; + final String address; + final String pubKey; + final String endpoint; + final String allowedIps; + final String? dns; + final bool? mfaEnabled; + final String? trafficMethod; + final int? mfaMethod; + final int keepAliveInterval; + final int? locationMfaMode; + const LocationsData({ + required this.id, + required this.instance, + required this.networkId, + required this.name, + required this.address, + required this.pubKey, + required this.endpoint, + required this.allowedIps, + this.dns, + this.mfaEnabled, + this.trafficMethod, + this.mfaMethod, + required this.keepAliveInterval, + this.locationMfaMode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['instance'] = Variable(instance); + map['network_id'] = Variable(networkId); + map['name'] = Variable(name); + map['address'] = Variable(address); + map['pub_key'] = Variable(pubKey); + map['endpoint'] = Variable(endpoint); + map['allowed_ips'] = Variable(allowedIps); + if (!nullToAbsent || dns != null) { + map['dns'] = Variable(dns); + } + if (!nullToAbsent || mfaEnabled != null) { + map['mfa_enabled'] = Variable(mfaEnabled); + } + if (!nullToAbsent || trafficMethod != null) { + map['traffic_method'] = Variable(trafficMethod); + } + if (!nullToAbsent || mfaMethod != null) { + map['mfa_method'] = Variable(mfaMethod); + } + map['keep_alive_interval'] = Variable(keepAliveInterval); + if (!nullToAbsent || locationMfaMode != null) { + map['location_mfa_mode'] = Variable(locationMfaMode); + } + return map; + } + + LocationsCompanion toCompanion(bool nullToAbsent) { + return LocationsCompanion( + id: Value(id), + instance: Value(instance), + networkId: Value(networkId), + name: Value(name), + address: Value(address), + pubKey: Value(pubKey), + endpoint: Value(endpoint), + allowedIps: Value(allowedIps), + dns: dns == null && nullToAbsent ? const Value.absent() : Value(dns), + mfaEnabled: mfaEnabled == null && nullToAbsent + ? const Value.absent() + : Value(mfaEnabled), + trafficMethod: trafficMethod == null && nullToAbsent + ? const Value.absent() + : Value(trafficMethod), + mfaMethod: mfaMethod == null && nullToAbsent + ? const Value.absent() + : Value(mfaMethod), + keepAliveInterval: Value(keepAliveInterval), + locationMfaMode: locationMfaMode == null && nullToAbsent + ? const Value.absent() + : Value(locationMfaMode), + ); + } + + factory LocationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocationsData( + id: serializer.fromJson(json['id']), + instance: serializer.fromJson(json['instance']), + networkId: serializer.fromJson(json['networkId']), + name: serializer.fromJson(json['name']), + address: serializer.fromJson(json['address']), + pubKey: serializer.fromJson(json['pubKey']), + endpoint: serializer.fromJson(json['endpoint']), + allowedIps: serializer.fromJson(json['allowedIps']), + dns: serializer.fromJson(json['dns']), + mfaEnabled: serializer.fromJson(json['mfaEnabled']), + trafficMethod: serializer.fromJson(json['trafficMethod']), + mfaMethod: serializer.fromJson(json['mfaMethod']), + keepAliveInterval: serializer.fromJson(json['keepAliveInterval']), + locationMfaMode: serializer.fromJson(json['locationMfaMode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'instance': serializer.toJson(instance), + 'networkId': serializer.toJson(networkId), + 'name': serializer.toJson(name), + 'address': serializer.toJson(address), + 'pubKey': serializer.toJson(pubKey), + 'endpoint': serializer.toJson(endpoint), + 'allowedIps': serializer.toJson(allowedIps), + 'dns': serializer.toJson(dns), + 'mfaEnabled': serializer.toJson(mfaEnabled), + 'trafficMethod': serializer.toJson(trafficMethod), + 'mfaMethod': serializer.toJson(mfaMethod), + 'keepAliveInterval': serializer.toJson(keepAliveInterval), + 'locationMfaMode': serializer.toJson(locationMfaMode), + }; + } + + LocationsData copyWith({ + int? id, + int? instance, + int? networkId, + String? name, + String? address, + String? pubKey, + String? endpoint, + String? allowedIps, + Value dns = const Value.absent(), + Value mfaEnabled = const Value.absent(), + Value trafficMethod = const Value.absent(), + Value mfaMethod = const Value.absent(), + int? keepAliveInterval, + Value locationMfaMode = const Value.absent(), + }) => LocationsData( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns.present ? dns.value : this.dns, + mfaEnabled: mfaEnabled.present ? mfaEnabled.value : this.mfaEnabled, + trafficMethod: trafficMethod.present + ? trafficMethod.value + : this.trafficMethod, + mfaMethod: mfaMethod.present ? mfaMethod.value : this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode.present + ? locationMfaMode.value + : this.locationMfaMode, + ); + LocationsData copyWithCompanion(LocationsCompanion data) { + return LocationsData( + id: data.id.present ? data.id.value : this.id, + instance: data.instance.present ? data.instance.value : this.instance, + networkId: data.networkId.present ? data.networkId.value : this.networkId, + name: data.name.present ? data.name.value : this.name, + address: data.address.present ? data.address.value : this.address, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + endpoint: data.endpoint.present ? data.endpoint.value : this.endpoint, + allowedIps: data.allowedIps.present + ? data.allowedIps.value + : this.allowedIps, + dns: data.dns.present ? data.dns.value : this.dns, + mfaEnabled: data.mfaEnabled.present + ? data.mfaEnabled.value + : this.mfaEnabled, + trafficMethod: data.trafficMethod.present + ? data.trafficMethod.value + : this.trafficMethod, + mfaMethod: data.mfaMethod.present ? data.mfaMethod.value : this.mfaMethod, + keepAliveInterval: data.keepAliveInterval.present + ? data.keepAliveInterval.value + : this.keepAliveInterval, + locationMfaMode: data.locationMfaMode.present + ? data.locationMfaMode.value + : this.locationMfaMode, + ); + } + + @override + String toString() { + return (StringBuffer('LocationsData(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocationsData && + other.id == this.id && + other.instance == this.instance && + other.networkId == this.networkId && + other.name == this.name && + other.address == this.address && + other.pubKey == this.pubKey && + other.endpoint == this.endpoint && + other.allowedIps == this.allowedIps && + other.dns == this.dns && + other.mfaEnabled == this.mfaEnabled && + other.trafficMethod == this.trafficMethod && + other.mfaMethod == this.mfaMethod && + other.keepAliveInterval == this.keepAliveInterval && + other.locationMfaMode == this.locationMfaMode); +} + +class LocationsCompanion extends UpdateCompanion { + final Value id; + final Value instance; + final Value networkId; + final Value name; + final Value address; + final Value pubKey; + final Value endpoint; + final Value allowedIps; + final Value dns; + final Value mfaEnabled; + final Value trafficMethod; + final Value mfaMethod; + final Value keepAliveInterval; + final Value locationMfaMode; + const LocationsCompanion({ + this.id = const Value.absent(), + this.instance = const Value.absent(), + this.networkId = const Value.absent(), + this.name = const Value.absent(), + this.address = const Value.absent(), + this.pubKey = const Value.absent(), + this.endpoint = const Value.absent(), + this.allowedIps = const Value.absent(), + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + this.keepAliveInterval = const Value.absent(), + this.locationMfaMode = const Value.absent(), + }); + LocationsCompanion.insert({ + this.id = const Value.absent(), + required int instance, + required int networkId, + required String name, + required String address, + required String pubKey, + required String endpoint, + required String allowedIps, + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + required int keepAliveInterval, + this.locationMfaMode = const Value.absent(), + }) : instance = Value(instance), + networkId = Value(networkId), + name = Value(name), + address = Value(address), + pubKey = Value(pubKey), + endpoint = Value(endpoint), + allowedIps = Value(allowedIps), + keepAliveInterval = Value(keepAliveInterval); + static Insertable custom({ + Expression? id, + Expression? instance, + Expression? networkId, + Expression? name, + Expression? address, + Expression? pubKey, + Expression? endpoint, + Expression? allowedIps, + Expression? dns, + Expression? mfaEnabled, + Expression? trafficMethod, + Expression? mfaMethod, + Expression? keepAliveInterval, + Expression? locationMfaMode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (instance != null) 'instance': instance, + if (networkId != null) 'network_id': networkId, + if (name != null) 'name': name, + if (address != null) 'address': address, + if (pubKey != null) 'pub_key': pubKey, + if (endpoint != null) 'endpoint': endpoint, + if (allowedIps != null) 'allowed_ips': allowedIps, + if (dns != null) 'dns': dns, + if (mfaEnabled != null) 'mfa_enabled': mfaEnabled, + if (trafficMethod != null) 'traffic_method': trafficMethod, + if (mfaMethod != null) 'mfa_method': mfaMethod, + if (keepAliveInterval != null) 'keep_alive_interval': keepAliveInterval, + if (locationMfaMode != null) 'location_mfa_mode': locationMfaMode, + }); + } + + LocationsCompanion copyWith({ + Value? id, + Value? instance, + Value? networkId, + Value? name, + Value? address, + Value? pubKey, + Value? endpoint, + Value? allowedIps, + Value? dns, + Value? mfaEnabled, + Value? trafficMethod, + Value? mfaMethod, + Value? keepAliveInterval, + Value? locationMfaMode, + }) { + return LocationsCompanion( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns ?? this.dns, + mfaEnabled: mfaEnabled ?? this.mfaEnabled, + trafficMethod: trafficMethod ?? this.trafficMethod, + mfaMethod: mfaMethod ?? this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode ?? this.locationMfaMode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (instance.present) { + map['instance'] = Variable(instance.value); + } + if (networkId.present) { + map['network_id'] = Variable(networkId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (address.present) { + map['address'] = Variable(address.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (endpoint.present) { + map['endpoint'] = Variable(endpoint.value); + } + if (allowedIps.present) { + map['allowed_ips'] = Variable(allowedIps.value); + } + if (dns.present) { + map['dns'] = Variable(dns.value); + } + if (mfaEnabled.present) { + map['mfa_enabled'] = Variable(mfaEnabled.value); + } + if (trafficMethod.present) { + map['traffic_method'] = Variable(trafficMethod.value); + } + if (mfaMethod.present) { + map['mfa_method'] = Variable(mfaMethod.value); + } + if (keepAliveInterval.present) { + map['keep_alive_interval'] = Variable(keepAliveInterval.value); + } + if (locationMfaMode.present) { + map['location_mfa_mode'] = Variable(locationMfaMode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocationsCompanion(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV2 extends GeneratedDatabase { + DatabaseAtV2(QueryExecutor e) : super(e); + late final DefguardInstances defguardInstances = DefguardInstances(this); + late final Locations locations = Locations(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + defguardInstances, + locations, + ]; + @override + int get schemaVersion => 2; +} diff --git a/client/test/drift/defguard/generated/schema_v3.dart b/client/test/drift/defguard/generated/schema_v3.dart new file mode 100644 index 0000000..54f693b --- /dev/null +++ b/client/test/drift/defguard/generated/schema_v3.dart @@ -0,0 +1,1321 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class DefguardInstances extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + DefguardInstances(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn uuid = GeneratedColumn( + 'uuid', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn url = GeneratedColumn( + 'url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn deviceId = GeneratedColumn( + 'device_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn proxyUrl = GeneratedColumn( + 'proxy_url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn poolingToken = GeneratedColumn( + 'pooling_token', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn clientTrafficPolicy = GeneratedColumn( + 'client_traffic_policy', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn enterpriseEnabled = GeneratedColumn( + 'enterprise_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("enterprise_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn privateKey = GeneratedColumn( + 'private_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn mfaKeysStored = GeneratedColumn( + 'mfa_keys_stored', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_keys_stored" IN (0, 1))', + ), + ); + late final GeneratedColumn openidDisplayName = + GeneratedColumn( + 'openid_display_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + clientTrafficPolicy, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + openidDisplayName, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'defguard_instances'; + @override + Set get $primaryKey => {id}; + @override + DefguardInstancesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return DefguardInstancesData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + uuid: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}uuid'], + )!, + url: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}url'], + )!, + deviceId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}device_id'], + )!, + proxyUrl: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}proxy_url'], + )!, + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + )!, + poolingToken: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pooling_token'], + )!, + clientTrafficPolicy: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}client_traffic_policy'], + )!, + enterpriseEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}enterprise_enabled'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + privateKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}private_key'], + )!, + mfaKeysStored: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_keys_stored'], + )!, + openidDisplayName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}openid_display_name'], + ), + ); + } + + @override + DefguardInstances createAlias(String alias) { + return DefguardInstances(attachedDatabase, alias); + } +} + +class DefguardInstancesData extends DataClass + implements Insertable { + final int id; + final String name; + final String uuid; + final String url; + final int deviceId; + final String proxyUrl; + final String username; + final String poolingToken; + final int clientTrafficPolicy; + final bool enterpriseEnabled; + final String pubKey; + final String privateKey; + final bool mfaKeysStored; + final String? openidDisplayName; + const DefguardInstancesData({ + required this.id, + required this.name, + required this.uuid, + required this.url, + required this.deviceId, + required this.proxyUrl, + required this.username, + required this.poolingToken, + required this.clientTrafficPolicy, + required this.enterpriseEnabled, + required this.pubKey, + required this.privateKey, + required this.mfaKeysStored, + this.openidDisplayName, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['uuid'] = Variable(uuid); + map['url'] = Variable(url); + map['device_id'] = Variable(deviceId); + map['proxy_url'] = Variable(proxyUrl); + map['username'] = Variable(username); + map['pooling_token'] = Variable(poolingToken); + map['client_traffic_policy'] = Variable(clientTrafficPolicy); + map['enterprise_enabled'] = Variable(enterpriseEnabled); + map['pub_key'] = Variable(pubKey); + map['private_key'] = Variable(privateKey); + map['mfa_keys_stored'] = Variable(mfaKeysStored); + if (!nullToAbsent || openidDisplayName != null) { + map['openid_display_name'] = Variable(openidDisplayName); + } + return map; + } + + DefguardInstancesCompanion toCompanion(bool nullToAbsent) { + return DefguardInstancesCompanion( + id: Value(id), + name: Value(name), + uuid: Value(uuid), + url: Value(url), + deviceId: Value(deviceId), + proxyUrl: Value(proxyUrl), + username: Value(username), + poolingToken: Value(poolingToken), + clientTrafficPolicy: Value(clientTrafficPolicy), + enterpriseEnabled: Value(enterpriseEnabled), + pubKey: Value(pubKey), + privateKey: Value(privateKey), + mfaKeysStored: Value(mfaKeysStored), + openidDisplayName: openidDisplayName == null && nullToAbsent + ? const Value.absent() + : Value(openidDisplayName), + ); + } + + factory DefguardInstancesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return DefguardInstancesData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + uuid: serializer.fromJson(json['uuid']), + url: serializer.fromJson(json['url']), + deviceId: serializer.fromJson(json['deviceId']), + proxyUrl: serializer.fromJson(json['proxyUrl']), + username: serializer.fromJson(json['username']), + poolingToken: serializer.fromJson(json['poolingToken']), + clientTrafficPolicy: serializer.fromJson( + json['clientTrafficPolicy'], + ), + enterpriseEnabled: serializer.fromJson(json['enterpriseEnabled']), + pubKey: serializer.fromJson(json['pubKey']), + privateKey: serializer.fromJson(json['privateKey']), + mfaKeysStored: serializer.fromJson(json['mfaKeysStored']), + openidDisplayName: serializer.fromJson( + json['openidDisplayName'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'uuid': serializer.toJson(uuid), + 'url': serializer.toJson(url), + 'deviceId': serializer.toJson(deviceId), + 'proxyUrl': serializer.toJson(proxyUrl), + 'username': serializer.toJson(username), + 'poolingToken': serializer.toJson(poolingToken), + 'clientTrafficPolicy': serializer.toJson(clientTrafficPolicy), + 'enterpriseEnabled': serializer.toJson(enterpriseEnabled), + 'pubKey': serializer.toJson(pubKey), + 'privateKey': serializer.toJson(privateKey), + 'mfaKeysStored': serializer.toJson(mfaKeysStored), + 'openidDisplayName': serializer.toJson(openidDisplayName), + }; + } + + DefguardInstancesData copyWith({ + int? id, + String? name, + String? uuid, + String? url, + int? deviceId, + String? proxyUrl, + String? username, + String? poolingToken, + int? clientTrafficPolicy, + bool? enterpriseEnabled, + String? pubKey, + String? privateKey, + bool? mfaKeysStored, + Value openidDisplayName = const Value.absent(), + }) => DefguardInstancesData( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + openidDisplayName: openidDisplayName.present + ? openidDisplayName.value + : this.openidDisplayName, + ); + DefguardInstancesData copyWithCompanion(DefguardInstancesCompanion data) { + return DefguardInstancesData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + uuid: data.uuid.present ? data.uuid.value : this.uuid, + url: data.url.present ? data.url.value : this.url, + deviceId: data.deviceId.present ? data.deviceId.value : this.deviceId, + proxyUrl: data.proxyUrl.present ? data.proxyUrl.value : this.proxyUrl, + username: data.username.present ? data.username.value : this.username, + poolingToken: data.poolingToken.present + ? data.poolingToken.value + : this.poolingToken, + clientTrafficPolicy: data.clientTrafficPolicy.present + ? data.clientTrafficPolicy.value + : this.clientTrafficPolicy, + enterpriseEnabled: data.enterpriseEnabled.present + ? data.enterpriseEnabled.value + : this.enterpriseEnabled, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + privateKey: data.privateKey.present + ? data.privateKey.value + : this.privateKey, + mfaKeysStored: data.mfaKeysStored.present + ? data.mfaKeysStored.value + : this.mfaKeysStored, + openidDisplayName: data.openidDisplayName.present + ? data.openidDisplayName.value + : this.openidDisplayName, + ); + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored, ') + ..write('openidDisplayName: $openidDisplayName') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + clientTrafficPolicy, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + openidDisplayName, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is DefguardInstancesData && + other.id == this.id && + other.name == this.name && + other.uuid == this.uuid && + other.url == this.url && + other.deviceId == this.deviceId && + other.proxyUrl == this.proxyUrl && + other.username == this.username && + other.poolingToken == this.poolingToken && + other.clientTrafficPolicy == this.clientTrafficPolicy && + other.enterpriseEnabled == this.enterpriseEnabled && + other.pubKey == this.pubKey && + other.privateKey == this.privateKey && + other.mfaKeysStored == this.mfaKeysStored && + other.openidDisplayName == this.openidDisplayName); +} + +class DefguardInstancesCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value uuid; + final Value url; + final Value deviceId; + final Value proxyUrl; + final Value username; + final Value poolingToken; + final Value clientTrafficPolicy; + final Value enterpriseEnabled; + final Value pubKey; + final Value privateKey; + final Value mfaKeysStored; + final Value openidDisplayName; + const DefguardInstancesCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.uuid = const Value.absent(), + this.url = const Value.absent(), + this.deviceId = const Value.absent(), + this.proxyUrl = const Value.absent(), + this.username = const Value.absent(), + this.poolingToken = const Value.absent(), + this.clientTrafficPolicy = const Value.absent(), + this.enterpriseEnabled = const Value.absent(), + this.pubKey = const Value.absent(), + this.privateKey = const Value.absent(), + this.mfaKeysStored = const Value.absent(), + this.openidDisplayName = const Value.absent(), + }); + DefguardInstancesCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String uuid, + required String url, + required int deviceId, + required String proxyUrl, + required String username, + required String poolingToken, + this.clientTrafficPolicy = const Value.absent(), + required bool enterpriseEnabled, + required String pubKey, + required String privateKey, + required bool mfaKeysStored, + this.openidDisplayName = const Value.absent(), + }) : name = Value(name), + uuid = Value(uuid), + url = Value(url), + deviceId = Value(deviceId), + proxyUrl = Value(proxyUrl), + username = Value(username), + poolingToken = Value(poolingToken), + enterpriseEnabled = Value(enterpriseEnabled), + pubKey = Value(pubKey), + privateKey = Value(privateKey), + mfaKeysStored = Value(mfaKeysStored); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? uuid, + Expression? url, + Expression? deviceId, + Expression? proxyUrl, + Expression? username, + Expression? poolingToken, + Expression? clientTrafficPolicy, + Expression? enterpriseEnabled, + Expression? pubKey, + Expression? privateKey, + Expression? mfaKeysStored, + Expression? openidDisplayName, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (uuid != null) 'uuid': uuid, + if (url != null) 'url': url, + if (deviceId != null) 'device_id': deviceId, + if (proxyUrl != null) 'proxy_url': proxyUrl, + if (username != null) 'username': username, + if (poolingToken != null) 'pooling_token': poolingToken, + if (clientTrafficPolicy != null) + 'client_traffic_policy': clientTrafficPolicy, + if (enterpriseEnabled != null) 'enterprise_enabled': enterpriseEnabled, + if (pubKey != null) 'pub_key': pubKey, + if (privateKey != null) 'private_key': privateKey, + if (mfaKeysStored != null) 'mfa_keys_stored': mfaKeysStored, + if (openidDisplayName != null) 'openid_display_name': openidDisplayName, + }); + } + + DefguardInstancesCompanion copyWith({ + Value? id, + Value? name, + Value? uuid, + Value? url, + Value? deviceId, + Value? proxyUrl, + Value? username, + Value? poolingToken, + Value? clientTrafficPolicy, + Value? enterpriseEnabled, + Value? pubKey, + Value? privateKey, + Value? mfaKeysStored, + Value? openidDisplayName, + }) { + return DefguardInstancesCompanion( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + openidDisplayName: openidDisplayName ?? this.openidDisplayName, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (uuid.present) { + map['uuid'] = Variable(uuid.value); + } + if (url.present) { + map['url'] = Variable(url.value); + } + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (proxyUrl.present) { + map['proxy_url'] = Variable(proxyUrl.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (poolingToken.present) { + map['pooling_token'] = Variable(poolingToken.value); + } + if (clientTrafficPolicy.present) { + map['client_traffic_policy'] = Variable(clientTrafficPolicy.value); + } + if (enterpriseEnabled.present) { + map['enterprise_enabled'] = Variable(enterpriseEnabled.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (privateKey.present) { + map['private_key'] = Variable(privateKey.value); + } + if (mfaKeysStored.present) { + map['mfa_keys_stored'] = Variable(mfaKeysStored.value); + } + if (openidDisplayName.present) { + map['openid_display_name'] = Variable(openidDisplayName.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored, ') + ..write('openidDisplayName: $openidDisplayName') + ..write(')')) + .toString(); + } +} + +class Locations extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Locations(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn instance = GeneratedColumn( + 'instance', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES defguard_instances (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn networkId = GeneratedColumn( + 'network_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn address = GeneratedColumn( + 'address', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn endpoint = GeneratedColumn( + 'endpoint', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn allowedIps = GeneratedColumn( + 'allowed_ips', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn dns = GeneratedColumn( + 'dns', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaEnabled = GeneratedColumn( + 'mfa_enabled', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn trafficMethod = GeneratedColumn( + 'traffic_method', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaMethod = GeneratedColumn( + 'mfa_method', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn keepAliveInterval = GeneratedColumn( + 'keep_alive_interval', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn locationMfaMode = GeneratedColumn( + 'location_mfa_mode', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'locations'; + @override + Set get $primaryKey => {id}; + @override + LocationsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocationsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + instance: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}instance'], + )!, + networkId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}network_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + address: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}address'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + endpoint: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}endpoint'], + )!, + allowedIps: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}allowed_ips'], + )!, + dns: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}dns'], + ), + mfaEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_enabled'], + ), + trafficMethod: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}traffic_method'], + ), + mfaMethod: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mfa_method'], + ), + keepAliveInterval: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}keep_alive_interval'], + )!, + locationMfaMode: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}location_mfa_mode'], + ), + ); + } + + @override + Locations createAlias(String alias) { + return Locations(attachedDatabase, alias); + } +} + +class LocationsData extends DataClass implements Insertable { + final int id; + final int instance; + final int networkId; + final String name; + final String address; + final String pubKey; + final String endpoint; + final String allowedIps; + final String? dns; + final bool? mfaEnabled; + final String? trafficMethod; + final int? mfaMethod; + final int keepAliveInterval; + final int? locationMfaMode; + const LocationsData({ + required this.id, + required this.instance, + required this.networkId, + required this.name, + required this.address, + required this.pubKey, + required this.endpoint, + required this.allowedIps, + this.dns, + this.mfaEnabled, + this.trafficMethod, + this.mfaMethod, + required this.keepAliveInterval, + this.locationMfaMode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['instance'] = Variable(instance); + map['network_id'] = Variable(networkId); + map['name'] = Variable(name); + map['address'] = Variable(address); + map['pub_key'] = Variable(pubKey); + map['endpoint'] = Variable(endpoint); + map['allowed_ips'] = Variable(allowedIps); + if (!nullToAbsent || dns != null) { + map['dns'] = Variable(dns); + } + if (!nullToAbsent || mfaEnabled != null) { + map['mfa_enabled'] = Variable(mfaEnabled); + } + if (!nullToAbsent || trafficMethod != null) { + map['traffic_method'] = Variable(trafficMethod); + } + if (!nullToAbsent || mfaMethod != null) { + map['mfa_method'] = Variable(mfaMethod); + } + map['keep_alive_interval'] = Variable(keepAliveInterval); + if (!nullToAbsent || locationMfaMode != null) { + map['location_mfa_mode'] = Variable(locationMfaMode); + } + return map; + } + + LocationsCompanion toCompanion(bool nullToAbsent) { + return LocationsCompanion( + id: Value(id), + instance: Value(instance), + networkId: Value(networkId), + name: Value(name), + address: Value(address), + pubKey: Value(pubKey), + endpoint: Value(endpoint), + allowedIps: Value(allowedIps), + dns: dns == null && nullToAbsent ? const Value.absent() : Value(dns), + mfaEnabled: mfaEnabled == null && nullToAbsent + ? const Value.absent() + : Value(mfaEnabled), + trafficMethod: trafficMethod == null && nullToAbsent + ? const Value.absent() + : Value(trafficMethod), + mfaMethod: mfaMethod == null && nullToAbsent + ? const Value.absent() + : Value(mfaMethod), + keepAliveInterval: Value(keepAliveInterval), + locationMfaMode: locationMfaMode == null && nullToAbsent + ? const Value.absent() + : Value(locationMfaMode), + ); + } + + factory LocationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocationsData( + id: serializer.fromJson(json['id']), + instance: serializer.fromJson(json['instance']), + networkId: serializer.fromJson(json['networkId']), + name: serializer.fromJson(json['name']), + address: serializer.fromJson(json['address']), + pubKey: serializer.fromJson(json['pubKey']), + endpoint: serializer.fromJson(json['endpoint']), + allowedIps: serializer.fromJson(json['allowedIps']), + dns: serializer.fromJson(json['dns']), + mfaEnabled: serializer.fromJson(json['mfaEnabled']), + trafficMethod: serializer.fromJson(json['trafficMethod']), + mfaMethod: serializer.fromJson(json['mfaMethod']), + keepAliveInterval: serializer.fromJson(json['keepAliveInterval']), + locationMfaMode: serializer.fromJson(json['locationMfaMode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'instance': serializer.toJson(instance), + 'networkId': serializer.toJson(networkId), + 'name': serializer.toJson(name), + 'address': serializer.toJson(address), + 'pubKey': serializer.toJson(pubKey), + 'endpoint': serializer.toJson(endpoint), + 'allowedIps': serializer.toJson(allowedIps), + 'dns': serializer.toJson(dns), + 'mfaEnabled': serializer.toJson(mfaEnabled), + 'trafficMethod': serializer.toJson(trafficMethod), + 'mfaMethod': serializer.toJson(mfaMethod), + 'keepAliveInterval': serializer.toJson(keepAliveInterval), + 'locationMfaMode': serializer.toJson(locationMfaMode), + }; + } + + LocationsData copyWith({ + int? id, + int? instance, + int? networkId, + String? name, + String? address, + String? pubKey, + String? endpoint, + String? allowedIps, + Value dns = const Value.absent(), + Value mfaEnabled = const Value.absent(), + Value trafficMethod = const Value.absent(), + Value mfaMethod = const Value.absent(), + int? keepAliveInterval, + Value locationMfaMode = const Value.absent(), + }) => LocationsData( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns.present ? dns.value : this.dns, + mfaEnabled: mfaEnabled.present ? mfaEnabled.value : this.mfaEnabled, + trafficMethod: trafficMethod.present + ? trafficMethod.value + : this.trafficMethod, + mfaMethod: mfaMethod.present ? mfaMethod.value : this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode.present + ? locationMfaMode.value + : this.locationMfaMode, + ); + LocationsData copyWithCompanion(LocationsCompanion data) { + return LocationsData( + id: data.id.present ? data.id.value : this.id, + instance: data.instance.present ? data.instance.value : this.instance, + networkId: data.networkId.present ? data.networkId.value : this.networkId, + name: data.name.present ? data.name.value : this.name, + address: data.address.present ? data.address.value : this.address, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + endpoint: data.endpoint.present ? data.endpoint.value : this.endpoint, + allowedIps: data.allowedIps.present + ? data.allowedIps.value + : this.allowedIps, + dns: data.dns.present ? data.dns.value : this.dns, + mfaEnabled: data.mfaEnabled.present + ? data.mfaEnabled.value + : this.mfaEnabled, + trafficMethod: data.trafficMethod.present + ? data.trafficMethod.value + : this.trafficMethod, + mfaMethod: data.mfaMethod.present ? data.mfaMethod.value : this.mfaMethod, + keepAliveInterval: data.keepAliveInterval.present + ? data.keepAliveInterval.value + : this.keepAliveInterval, + locationMfaMode: data.locationMfaMode.present + ? data.locationMfaMode.value + : this.locationMfaMode, + ); + } + + @override + String toString() { + return (StringBuffer('LocationsData(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocationsData && + other.id == this.id && + other.instance == this.instance && + other.networkId == this.networkId && + other.name == this.name && + other.address == this.address && + other.pubKey == this.pubKey && + other.endpoint == this.endpoint && + other.allowedIps == this.allowedIps && + other.dns == this.dns && + other.mfaEnabled == this.mfaEnabled && + other.trafficMethod == this.trafficMethod && + other.mfaMethod == this.mfaMethod && + other.keepAliveInterval == this.keepAliveInterval && + other.locationMfaMode == this.locationMfaMode); +} + +class LocationsCompanion extends UpdateCompanion { + final Value id; + final Value instance; + final Value networkId; + final Value name; + final Value address; + final Value pubKey; + final Value endpoint; + final Value allowedIps; + final Value dns; + final Value mfaEnabled; + final Value trafficMethod; + final Value mfaMethod; + final Value keepAliveInterval; + final Value locationMfaMode; + const LocationsCompanion({ + this.id = const Value.absent(), + this.instance = const Value.absent(), + this.networkId = const Value.absent(), + this.name = const Value.absent(), + this.address = const Value.absent(), + this.pubKey = const Value.absent(), + this.endpoint = const Value.absent(), + this.allowedIps = const Value.absent(), + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + this.keepAliveInterval = const Value.absent(), + this.locationMfaMode = const Value.absent(), + }); + LocationsCompanion.insert({ + this.id = const Value.absent(), + required int instance, + required int networkId, + required String name, + required String address, + required String pubKey, + required String endpoint, + required String allowedIps, + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + required int keepAliveInterval, + this.locationMfaMode = const Value.absent(), + }) : instance = Value(instance), + networkId = Value(networkId), + name = Value(name), + address = Value(address), + pubKey = Value(pubKey), + endpoint = Value(endpoint), + allowedIps = Value(allowedIps), + keepAliveInterval = Value(keepAliveInterval); + static Insertable custom({ + Expression? id, + Expression? instance, + Expression? networkId, + Expression? name, + Expression? address, + Expression? pubKey, + Expression? endpoint, + Expression? allowedIps, + Expression? dns, + Expression? mfaEnabled, + Expression? trafficMethod, + Expression? mfaMethod, + Expression? keepAliveInterval, + Expression? locationMfaMode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (instance != null) 'instance': instance, + if (networkId != null) 'network_id': networkId, + if (name != null) 'name': name, + if (address != null) 'address': address, + if (pubKey != null) 'pub_key': pubKey, + if (endpoint != null) 'endpoint': endpoint, + if (allowedIps != null) 'allowed_ips': allowedIps, + if (dns != null) 'dns': dns, + if (mfaEnabled != null) 'mfa_enabled': mfaEnabled, + if (trafficMethod != null) 'traffic_method': trafficMethod, + if (mfaMethod != null) 'mfa_method': mfaMethod, + if (keepAliveInterval != null) 'keep_alive_interval': keepAliveInterval, + if (locationMfaMode != null) 'location_mfa_mode': locationMfaMode, + }); + } + + LocationsCompanion copyWith({ + Value? id, + Value? instance, + Value? networkId, + Value? name, + Value? address, + Value? pubKey, + Value? endpoint, + Value? allowedIps, + Value? dns, + Value? mfaEnabled, + Value? trafficMethod, + Value? mfaMethod, + Value? keepAliveInterval, + Value? locationMfaMode, + }) { + return LocationsCompanion( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns ?? this.dns, + mfaEnabled: mfaEnabled ?? this.mfaEnabled, + trafficMethod: trafficMethod ?? this.trafficMethod, + mfaMethod: mfaMethod ?? this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode ?? this.locationMfaMode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (instance.present) { + map['instance'] = Variable(instance.value); + } + if (networkId.present) { + map['network_id'] = Variable(networkId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (address.present) { + map['address'] = Variable(address.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (endpoint.present) { + map['endpoint'] = Variable(endpoint.value); + } + if (allowedIps.present) { + map['allowed_ips'] = Variable(allowedIps.value); + } + if (dns.present) { + map['dns'] = Variable(dns.value); + } + if (mfaEnabled.present) { + map['mfa_enabled'] = Variable(mfaEnabled.value); + } + if (trafficMethod.present) { + map['traffic_method'] = Variable(trafficMethod.value); + } + if (mfaMethod.present) { + map['mfa_method'] = Variable(mfaMethod.value); + } + if (keepAliveInterval.present) { + map['keep_alive_interval'] = Variable(keepAliveInterval.value); + } + if (locationMfaMode.present) { + map['location_mfa_mode'] = Variable(locationMfaMode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocationsCompanion(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV3 extends GeneratedDatabase { + DatabaseAtV3(QueryExecutor e) : super(e); + late final DefguardInstances defguardInstances = DefguardInstances(this); + late final Locations locations = Locations(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + defguardInstances, + locations, + ]; + @override + int get schemaVersion => 3; +} diff --git a/client/test/drift/defguard/generated/schema_v4.dart b/client/test/drift/defguard/generated/schema_v4.dart new file mode 100644 index 0000000..c6c2fcc --- /dev/null +++ b/client/test/drift/defguard/generated/schema_v4.dart @@ -0,0 +1,1372 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class DefguardInstances extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + DefguardInstances(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn uuid = GeneratedColumn( + 'uuid', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn url = GeneratedColumn( + 'url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn deviceId = GeneratedColumn( + 'device_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn proxyUrl = GeneratedColumn( + 'proxy_url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn poolingToken = GeneratedColumn( + 'pooling_token', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn clientTrafficPolicy = GeneratedColumn( + 'client_traffic_policy', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn enterpriseEnabled = GeneratedColumn( + 'enterprise_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("enterprise_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn privateKey = GeneratedColumn( + 'private_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn mfaKeysStored = GeneratedColumn( + 'mfa_keys_stored', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_keys_stored" IN (0, 1))', + ), + ); + late final GeneratedColumn openidDisplayName = + GeneratedColumn( + 'openid_display_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + clientTrafficPolicy, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + openidDisplayName, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'defguard_instances'; + @override + Set get $primaryKey => {id}; + @override + DefguardInstancesData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return DefguardInstancesData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + uuid: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}uuid'], + )!, + url: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}url'], + )!, + deviceId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}device_id'], + )!, + proxyUrl: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}proxy_url'], + )!, + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + )!, + poolingToken: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pooling_token'], + )!, + clientTrafficPolicy: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}client_traffic_policy'], + )!, + enterpriseEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}enterprise_enabled'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + privateKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}private_key'], + )!, + mfaKeysStored: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_keys_stored'], + )!, + openidDisplayName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}openid_display_name'], + ), + ); + } + + @override + DefguardInstances createAlias(String alias) { + return DefguardInstances(attachedDatabase, alias); + } +} + +class DefguardInstancesData extends DataClass + implements Insertable { + final int id; + final String name; + final String uuid; + final String url; + final int deviceId; + final String proxyUrl; + final String username; + final String poolingToken; + final int clientTrafficPolicy; + final bool enterpriseEnabled; + final String pubKey; + final String privateKey; + final bool mfaKeysStored; + final String? openidDisplayName; + const DefguardInstancesData({ + required this.id, + required this.name, + required this.uuid, + required this.url, + required this.deviceId, + required this.proxyUrl, + required this.username, + required this.poolingToken, + required this.clientTrafficPolicy, + required this.enterpriseEnabled, + required this.pubKey, + required this.privateKey, + required this.mfaKeysStored, + this.openidDisplayName, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['uuid'] = Variable(uuid); + map['url'] = Variable(url); + map['device_id'] = Variable(deviceId); + map['proxy_url'] = Variable(proxyUrl); + map['username'] = Variable(username); + map['pooling_token'] = Variable(poolingToken); + map['client_traffic_policy'] = Variable(clientTrafficPolicy); + map['enterprise_enabled'] = Variable(enterpriseEnabled); + map['pub_key'] = Variable(pubKey); + map['private_key'] = Variable(privateKey); + map['mfa_keys_stored'] = Variable(mfaKeysStored); + if (!nullToAbsent || openidDisplayName != null) { + map['openid_display_name'] = Variable(openidDisplayName); + } + return map; + } + + DefguardInstancesCompanion toCompanion(bool nullToAbsent) { + return DefguardInstancesCompanion( + id: Value(id), + name: Value(name), + uuid: Value(uuid), + url: Value(url), + deviceId: Value(deviceId), + proxyUrl: Value(proxyUrl), + username: Value(username), + poolingToken: Value(poolingToken), + clientTrafficPolicy: Value(clientTrafficPolicy), + enterpriseEnabled: Value(enterpriseEnabled), + pubKey: Value(pubKey), + privateKey: Value(privateKey), + mfaKeysStored: Value(mfaKeysStored), + openidDisplayName: openidDisplayName == null && nullToAbsent + ? const Value.absent() + : Value(openidDisplayName), + ); + } + + factory DefguardInstancesData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return DefguardInstancesData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + uuid: serializer.fromJson(json['uuid']), + url: serializer.fromJson(json['url']), + deviceId: serializer.fromJson(json['deviceId']), + proxyUrl: serializer.fromJson(json['proxyUrl']), + username: serializer.fromJson(json['username']), + poolingToken: serializer.fromJson(json['poolingToken']), + clientTrafficPolicy: serializer.fromJson( + json['clientTrafficPolicy'], + ), + enterpriseEnabled: serializer.fromJson(json['enterpriseEnabled']), + pubKey: serializer.fromJson(json['pubKey']), + privateKey: serializer.fromJson(json['privateKey']), + mfaKeysStored: serializer.fromJson(json['mfaKeysStored']), + openidDisplayName: serializer.fromJson( + json['openidDisplayName'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'uuid': serializer.toJson(uuid), + 'url': serializer.toJson(url), + 'deviceId': serializer.toJson(deviceId), + 'proxyUrl': serializer.toJson(proxyUrl), + 'username': serializer.toJson(username), + 'poolingToken': serializer.toJson(poolingToken), + 'clientTrafficPolicy': serializer.toJson(clientTrafficPolicy), + 'enterpriseEnabled': serializer.toJson(enterpriseEnabled), + 'pubKey': serializer.toJson(pubKey), + 'privateKey': serializer.toJson(privateKey), + 'mfaKeysStored': serializer.toJson(mfaKeysStored), + 'openidDisplayName': serializer.toJson(openidDisplayName), + }; + } + + DefguardInstancesData copyWith({ + int? id, + String? name, + String? uuid, + String? url, + int? deviceId, + String? proxyUrl, + String? username, + String? poolingToken, + int? clientTrafficPolicy, + bool? enterpriseEnabled, + String? pubKey, + String? privateKey, + bool? mfaKeysStored, + Value openidDisplayName = const Value.absent(), + }) => DefguardInstancesData( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + openidDisplayName: openidDisplayName.present + ? openidDisplayName.value + : this.openidDisplayName, + ); + DefguardInstancesData copyWithCompanion(DefguardInstancesCompanion data) { + return DefguardInstancesData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + uuid: data.uuid.present ? data.uuid.value : this.uuid, + url: data.url.present ? data.url.value : this.url, + deviceId: data.deviceId.present ? data.deviceId.value : this.deviceId, + proxyUrl: data.proxyUrl.present ? data.proxyUrl.value : this.proxyUrl, + username: data.username.present ? data.username.value : this.username, + poolingToken: data.poolingToken.present + ? data.poolingToken.value + : this.poolingToken, + clientTrafficPolicy: data.clientTrafficPolicy.present + ? data.clientTrafficPolicy.value + : this.clientTrafficPolicy, + enterpriseEnabled: data.enterpriseEnabled.present + ? data.enterpriseEnabled.value + : this.enterpriseEnabled, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + privateKey: data.privateKey.present + ? data.privateKey.value + : this.privateKey, + mfaKeysStored: data.mfaKeysStored.present + ? data.mfaKeysStored.value + : this.mfaKeysStored, + openidDisplayName: data.openidDisplayName.present + ? data.openidDisplayName.value + : this.openidDisplayName, + ); + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored, ') + ..write('openidDisplayName: $openidDisplayName') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + uuid, + url, + deviceId, + proxyUrl, + username, + poolingToken, + clientTrafficPolicy, + enterpriseEnabled, + pubKey, + privateKey, + mfaKeysStored, + openidDisplayName, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is DefguardInstancesData && + other.id == this.id && + other.name == this.name && + other.uuid == this.uuid && + other.url == this.url && + other.deviceId == this.deviceId && + other.proxyUrl == this.proxyUrl && + other.username == this.username && + other.poolingToken == this.poolingToken && + other.clientTrafficPolicy == this.clientTrafficPolicy && + other.enterpriseEnabled == this.enterpriseEnabled && + other.pubKey == this.pubKey && + other.privateKey == this.privateKey && + other.mfaKeysStored == this.mfaKeysStored && + other.openidDisplayName == this.openidDisplayName); +} + +class DefguardInstancesCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value uuid; + final Value url; + final Value deviceId; + final Value proxyUrl; + final Value username; + final Value poolingToken; + final Value clientTrafficPolicy; + final Value enterpriseEnabled; + final Value pubKey; + final Value privateKey; + final Value mfaKeysStored; + final Value openidDisplayName; + const DefguardInstancesCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.uuid = const Value.absent(), + this.url = const Value.absent(), + this.deviceId = const Value.absent(), + this.proxyUrl = const Value.absent(), + this.username = const Value.absent(), + this.poolingToken = const Value.absent(), + this.clientTrafficPolicy = const Value.absent(), + this.enterpriseEnabled = const Value.absent(), + this.pubKey = const Value.absent(), + this.privateKey = const Value.absent(), + this.mfaKeysStored = const Value.absent(), + this.openidDisplayName = const Value.absent(), + }); + DefguardInstancesCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String uuid, + required String url, + required int deviceId, + required String proxyUrl, + required String username, + required String poolingToken, + this.clientTrafficPolicy = const Value.absent(), + required bool enterpriseEnabled, + required String pubKey, + required String privateKey, + required bool mfaKeysStored, + this.openidDisplayName = const Value.absent(), + }) : name = Value(name), + uuid = Value(uuid), + url = Value(url), + deviceId = Value(deviceId), + proxyUrl = Value(proxyUrl), + username = Value(username), + poolingToken = Value(poolingToken), + enterpriseEnabled = Value(enterpriseEnabled), + pubKey = Value(pubKey), + privateKey = Value(privateKey), + mfaKeysStored = Value(mfaKeysStored); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? uuid, + Expression? url, + Expression? deviceId, + Expression? proxyUrl, + Expression? username, + Expression? poolingToken, + Expression? clientTrafficPolicy, + Expression? enterpriseEnabled, + Expression? pubKey, + Expression? privateKey, + Expression? mfaKeysStored, + Expression? openidDisplayName, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (uuid != null) 'uuid': uuid, + if (url != null) 'url': url, + if (deviceId != null) 'device_id': deviceId, + if (proxyUrl != null) 'proxy_url': proxyUrl, + if (username != null) 'username': username, + if (poolingToken != null) 'pooling_token': poolingToken, + if (clientTrafficPolicy != null) + 'client_traffic_policy': clientTrafficPolicy, + if (enterpriseEnabled != null) 'enterprise_enabled': enterpriseEnabled, + if (pubKey != null) 'pub_key': pubKey, + if (privateKey != null) 'private_key': privateKey, + if (mfaKeysStored != null) 'mfa_keys_stored': mfaKeysStored, + if (openidDisplayName != null) 'openid_display_name': openidDisplayName, + }); + } + + DefguardInstancesCompanion copyWith({ + Value? id, + Value? name, + Value? uuid, + Value? url, + Value? deviceId, + Value? proxyUrl, + Value? username, + Value? poolingToken, + Value? clientTrafficPolicy, + Value? enterpriseEnabled, + Value? pubKey, + Value? privateKey, + Value? mfaKeysStored, + Value? openidDisplayName, + }) { + return DefguardInstancesCompanion( + id: id ?? this.id, + name: name ?? this.name, + uuid: uuid ?? this.uuid, + url: url ?? this.url, + deviceId: deviceId ?? this.deviceId, + proxyUrl: proxyUrl ?? this.proxyUrl, + username: username ?? this.username, + poolingToken: poolingToken ?? this.poolingToken, + clientTrafficPolicy: clientTrafficPolicy ?? this.clientTrafficPolicy, + enterpriseEnabled: enterpriseEnabled ?? this.enterpriseEnabled, + pubKey: pubKey ?? this.pubKey, + privateKey: privateKey ?? this.privateKey, + mfaKeysStored: mfaKeysStored ?? this.mfaKeysStored, + openidDisplayName: openidDisplayName ?? this.openidDisplayName, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (uuid.present) { + map['uuid'] = Variable(uuid.value); + } + if (url.present) { + map['url'] = Variable(url.value); + } + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (proxyUrl.present) { + map['proxy_url'] = Variable(proxyUrl.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (poolingToken.present) { + map['pooling_token'] = Variable(poolingToken.value); + } + if (clientTrafficPolicy.present) { + map['client_traffic_policy'] = Variable(clientTrafficPolicy.value); + } + if (enterpriseEnabled.present) { + map['enterprise_enabled'] = Variable(enterpriseEnabled.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (privateKey.present) { + map['private_key'] = Variable(privateKey.value); + } + if (mfaKeysStored.present) { + map['mfa_keys_stored'] = Variable(mfaKeysStored.value); + } + if (openidDisplayName.present) { + map['openid_display_name'] = Variable(openidDisplayName.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('DefguardInstancesCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('uuid: $uuid, ') + ..write('url: $url, ') + ..write('deviceId: $deviceId, ') + ..write('proxyUrl: $proxyUrl, ') + ..write('username: $username, ') + ..write('poolingToken: $poolingToken, ') + ..write('clientTrafficPolicy: $clientTrafficPolicy, ') + ..write('enterpriseEnabled: $enterpriseEnabled, ') + ..write('pubKey: $pubKey, ') + ..write('privateKey: $privateKey, ') + ..write('mfaKeysStored: $mfaKeysStored, ') + ..write('openidDisplayName: $openidDisplayName') + ..write(')')) + .toString(); + } +} + +class Locations extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Locations(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn instance = GeneratedColumn( + 'instance', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES defguard_instances (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn networkId = GeneratedColumn( + 'network_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn address = GeneratedColumn( + 'address', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn pubKey = GeneratedColumn( + 'pub_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn endpoint = GeneratedColumn( + 'endpoint', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn allowedIps = GeneratedColumn( + 'allowed_ips', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn dns = GeneratedColumn( + 'dns', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaEnabled = GeneratedColumn( + 'mfa_enabled', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("mfa_enabled" IN (0, 1))', + ), + ); + late final GeneratedColumn trafficMethod = GeneratedColumn( + 'traffic_method', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn mfaMethod = GeneratedColumn( + 'mfa_method', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn keepAliveInterval = GeneratedColumn( + 'keep_alive_interval', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn locationMfaMode = GeneratedColumn( + 'location_mfa_mode', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn postureCheckRequired = GeneratedColumn( + 'posture_check_required', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("posture_check_required" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + postureCheckRequired, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'locations'; + @override + Set get $primaryKey => {id}; + @override + LocationsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocationsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + instance: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}instance'], + )!, + networkId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}network_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + address: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}address'], + )!, + pubKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pub_key'], + )!, + endpoint: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}endpoint'], + )!, + allowedIps: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}allowed_ips'], + )!, + dns: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}dns'], + ), + mfaEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}mfa_enabled'], + ), + trafficMethod: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}traffic_method'], + ), + mfaMethod: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}mfa_method'], + ), + keepAliveInterval: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}keep_alive_interval'], + )!, + locationMfaMode: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}location_mfa_mode'], + ), + postureCheckRequired: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}posture_check_required'], + ), + ); + } + + @override + Locations createAlias(String alias) { + return Locations(attachedDatabase, alias); + } +} + +class LocationsData extends DataClass implements Insertable { + final int id; + final int instance; + final int networkId; + final String name; + final String address; + final String pubKey; + final String endpoint; + final String allowedIps; + final String? dns; + final bool? mfaEnabled; + final String? trafficMethod; + final int? mfaMethod; + final int keepAliveInterval; + final int? locationMfaMode; + final bool? postureCheckRequired; + const LocationsData({ + required this.id, + required this.instance, + required this.networkId, + required this.name, + required this.address, + required this.pubKey, + required this.endpoint, + required this.allowedIps, + this.dns, + this.mfaEnabled, + this.trafficMethod, + this.mfaMethod, + required this.keepAliveInterval, + this.locationMfaMode, + this.postureCheckRequired, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['instance'] = Variable(instance); + map['network_id'] = Variable(networkId); + map['name'] = Variable(name); + map['address'] = Variable(address); + map['pub_key'] = Variable(pubKey); + map['endpoint'] = Variable(endpoint); + map['allowed_ips'] = Variable(allowedIps); + if (!nullToAbsent || dns != null) { + map['dns'] = Variable(dns); + } + if (!nullToAbsent || mfaEnabled != null) { + map['mfa_enabled'] = Variable(mfaEnabled); + } + if (!nullToAbsent || trafficMethod != null) { + map['traffic_method'] = Variable(trafficMethod); + } + if (!nullToAbsent || mfaMethod != null) { + map['mfa_method'] = Variable(mfaMethod); + } + map['keep_alive_interval'] = Variable(keepAliveInterval); + if (!nullToAbsent || locationMfaMode != null) { + map['location_mfa_mode'] = Variable(locationMfaMode); + } + if (!nullToAbsent || postureCheckRequired != null) { + map['posture_check_required'] = Variable(postureCheckRequired); + } + return map; + } + + LocationsCompanion toCompanion(bool nullToAbsent) { + return LocationsCompanion( + id: Value(id), + instance: Value(instance), + networkId: Value(networkId), + name: Value(name), + address: Value(address), + pubKey: Value(pubKey), + endpoint: Value(endpoint), + allowedIps: Value(allowedIps), + dns: dns == null && nullToAbsent ? const Value.absent() : Value(dns), + mfaEnabled: mfaEnabled == null && nullToAbsent + ? const Value.absent() + : Value(mfaEnabled), + trafficMethod: trafficMethod == null && nullToAbsent + ? const Value.absent() + : Value(trafficMethod), + mfaMethod: mfaMethod == null && nullToAbsent + ? const Value.absent() + : Value(mfaMethod), + keepAliveInterval: Value(keepAliveInterval), + locationMfaMode: locationMfaMode == null && nullToAbsent + ? const Value.absent() + : Value(locationMfaMode), + postureCheckRequired: postureCheckRequired == null && nullToAbsent + ? const Value.absent() + : Value(postureCheckRequired), + ); + } + + factory LocationsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocationsData( + id: serializer.fromJson(json['id']), + instance: serializer.fromJson(json['instance']), + networkId: serializer.fromJson(json['networkId']), + name: serializer.fromJson(json['name']), + address: serializer.fromJson(json['address']), + pubKey: serializer.fromJson(json['pubKey']), + endpoint: serializer.fromJson(json['endpoint']), + allowedIps: serializer.fromJson(json['allowedIps']), + dns: serializer.fromJson(json['dns']), + mfaEnabled: serializer.fromJson(json['mfaEnabled']), + trafficMethod: serializer.fromJson(json['trafficMethod']), + mfaMethod: serializer.fromJson(json['mfaMethod']), + keepAliveInterval: serializer.fromJson(json['keepAliveInterval']), + locationMfaMode: serializer.fromJson(json['locationMfaMode']), + postureCheckRequired: serializer.fromJson( + json['postureCheckRequired'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'instance': serializer.toJson(instance), + 'networkId': serializer.toJson(networkId), + 'name': serializer.toJson(name), + 'address': serializer.toJson(address), + 'pubKey': serializer.toJson(pubKey), + 'endpoint': serializer.toJson(endpoint), + 'allowedIps': serializer.toJson(allowedIps), + 'dns': serializer.toJson(dns), + 'mfaEnabled': serializer.toJson(mfaEnabled), + 'trafficMethod': serializer.toJson(trafficMethod), + 'mfaMethod': serializer.toJson(mfaMethod), + 'keepAliveInterval': serializer.toJson(keepAliveInterval), + 'locationMfaMode': serializer.toJson(locationMfaMode), + 'postureCheckRequired': serializer.toJson(postureCheckRequired), + }; + } + + LocationsData copyWith({ + int? id, + int? instance, + int? networkId, + String? name, + String? address, + String? pubKey, + String? endpoint, + String? allowedIps, + Value dns = const Value.absent(), + Value mfaEnabled = const Value.absent(), + Value trafficMethod = const Value.absent(), + Value mfaMethod = const Value.absent(), + int? keepAliveInterval, + Value locationMfaMode = const Value.absent(), + Value postureCheckRequired = const Value.absent(), + }) => LocationsData( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns.present ? dns.value : this.dns, + mfaEnabled: mfaEnabled.present ? mfaEnabled.value : this.mfaEnabled, + trafficMethod: trafficMethod.present + ? trafficMethod.value + : this.trafficMethod, + mfaMethod: mfaMethod.present ? mfaMethod.value : this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode.present + ? locationMfaMode.value + : this.locationMfaMode, + postureCheckRequired: postureCheckRequired.present + ? postureCheckRequired.value + : this.postureCheckRequired, + ); + LocationsData copyWithCompanion(LocationsCompanion data) { + return LocationsData( + id: data.id.present ? data.id.value : this.id, + instance: data.instance.present ? data.instance.value : this.instance, + networkId: data.networkId.present ? data.networkId.value : this.networkId, + name: data.name.present ? data.name.value : this.name, + address: data.address.present ? data.address.value : this.address, + pubKey: data.pubKey.present ? data.pubKey.value : this.pubKey, + endpoint: data.endpoint.present ? data.endpoint.value : this.endpoint, + allowedIps: data.allowedIps.present + ? data.allowedIps.value + : this.allowedIps, + dns: data.dns.present ? data.dns.value : this.dns, + mfaEnabled: data.mfaEnabled.present + ? data.mfaEnabled.value + : this.mfaEnabled, + trafficMethod: data.trafficMethod.present + ? data.trafficMethod.value + : this.trafficMethod, + mfaMethod: data.mfaMethod.present ? data.mfaMethod.value : this.mfaMethod, + keepAliveInterval: data.keepAliveInterval.present + ? data.keepAliveInterval.value + : this.keepAliveInterval, + locationMfaMode: data.locationMfaMode.present + ? data.locationMfaMode.value + : this.locationMfaMode, + postureCheckRequired: data.postureCheckRequired.present + ? data.postureCheckRequired.value + : this.postureCheckRequired, + ); + } + + @override + String toString() { + return (StringBuffer('LocationsData(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode, ') + ..write('postureCheckRequired: $postureCheckRequired') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + instance, + networkId, + name, + address, + pubKey, + endpoint, + allowedIps, + dns, + mfaEnabled, + trafficMethod, + mfaMethod, + keepAliveInterval, + locationMfaMode, + postureCheckRequired, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocationsData && + other.id == this.id && + other.instance == this.instance && + other.networkId == this.networkId && + other.name == this.name && + other.address == this.address && + other.pubKey == this.pubKey && + other.endpoint == this.endpoint && + other.allowedIps == this.allowedIps && + other.dns == this.dns && + other.mfaEnabled == this.mfaEnabled && + other.trafficMethod == this.trafficMethod && + other.mfaMethod == this.mfaMethod && + other.keepAliveInterval == this.keepAliveInterval && + other.locationMfaMode == this.locationMfaMode && + other.postureCheckRequired == this.postureCheckRequired); +} + +class LocationsCompanion extends UpdateCompanion { + final Value id; + final Value instance; + final Value networkId; + final Value name; + final Value address; + final Value pubKey; + final Value endpoint; + final Value allowedIps; + final Value dns; + final Value mfaEnabled; + final Value trafficMethod; + final Value mfaMethod; + final Value keepAliveInterval; + final Value locationMfaMode; + final Value postureCheckRequired; + const LocationsCompanion({ + this.id = const Value.absent(), + this.instance = const Value.absent(), + this.networkId = const Value.absent(), + this.name = const Value.absent(), + this.address = const Value.absent(), + this.pubKey = const Value.absent(), + this.endpoint = const Value.absent(), + this.allowedIps = const Value.absent(), + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + this.keepAliveInterval = const Value.absent(), + this.locationMfaMode = const Value.absent(), + this.postureCheckRequired = const Value.absent(), + }); + LocationsCompanion.insert({ + this.id = const Value.absent(), + required int instance, + required int networkId, + required String name, + required String address, + required String pubKey, + required String endpoint, + required String allowedIps, + this.dns = const Value.absent(), + this.mfaEnabled = const Value.absent(), + this.trafficMethod = const Value.absent(), + this.mfaMethod = const Value.absent(), + required int keepAliveInterval, + this.locationMfaMode = const Value.absent(), + this.postureCheckRequired = const Value.absent(), + }) : instance = Value(instance), + networkId = Value(networkId), + name = Value(name), + address = Value(address), + pubKey = Value(pubKey), + endpoint = Value(endpoint), + allowedIps = Value(allowedIps), + keepAliveInterval = Value(keepAliveInterval); + static Insertable custom({ + Expression? id, + Expression? instance, + Expression? networkId, + Expression? name, + Expression? address, + Expression? pubKey, + Expression? endpoint, + Expression? allowedIps, + Expression? dns, + Expression? mfaEnabled, + Expression? trafficMethod, + Expression? mfaMethod, + Expression? keepAliveInterval, + Expression? locationMfaMode, + Expression? postureCheckRequired, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (instance != null) 'instance': instance, + if (networkId != null) 'network_id': networkId, + if (name != null) 'name': name, + if (address != null) 'address': address, + if (pubKey != null) 'pub_key': pubKey, + if (endpoint != null) 'endpoint': endpoint, + if (allowedIps != null) 'allowed_ips': allowedIps, + if (dns != null) 'dns': dns, + if (mfaEnabled != null) 'mfa_enabled': mfaEnabled, + if (trafficMethod != null) 'traffic_method': trafficMethod, + if (mfaMethod != null) 'mfa_method': mfaMethod, + if (keepAliveInterval != null) 'keep_alive_interval': keepAliveInterval, + if (locationMfaMode != null) 'location_mfa_mode': locationMfaMode, + if (postureCheckRequired != null) + 'posture_check_required': postureCheckRequired, + }); + } + + LocationsCompanion copyWith({ + Value? id, + Value? instance, + Value? networkId, + Value? name, + Value? address, + Value? pubKey, + Value? endpoint, + Value? allowedIps, + Value? dns, + Value? mfaEnabled, + Value? trafficMethod, + Value? mfaMethod, + Value? keepAliveInterval, + Value? locationMfaMode, + Value? postureCheckRequired, + }) { + return LocationsCompanion( + id: id ?? this.id, + instance: instance ?? this.instance, + networkId: networkId ?? this.networkId, + name: name ?? this.name, + address: address ?? this.address, + pubKey: pubKey ?? this.pubKey, + endpoint: endpoint ?? this.endpoint, + allowedIps: allowedIps ?? this.allowedIps, + dns: dns ?? this.dns, + mfaEnabled: mfaEnabled ?? this.mfaEnabled, + trafficMethod: trafficMethod ?? this.trafficMethod, + mfaMethod: mfaMethod ?? this.mfaMethod, + keepAliveInterval: keepAliveInterval ?? this.keepAliveInterval, + locationMfaMode: locationMfaMode ?? this.locationMfaMode, + postureCheckRequired: postureCheckRequired ?? this.postureCheckRequired, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (instance.present) { + map['instance'] = Variable(instance.value); + } + if (networkId.present) { + map['network_id'] = Variable(networkId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (address.present) { + map['address'] = Variable(address.value); + } + if (pubKey.present) { + map['pub_key'] = Variable(pubKey.value); + } + if (endpoint.present) { + map['endpoint'] = Variable(endpoint.value); + } + if (allowedIps.present) { + map['allowed_ips'] = Variable(allowedIps.value); + } + if (dns.present) { + map['dns'] = Variable(dns.value); + } + if (mfaEnabled.present) { + map['mfa_enabled'] = Variable(mfaEnabled.value); + } + if (trafficMethod.present) { + map['traffic_method'] = Variable(trafficMethod.value); + } + if (mfaMethod.present) { + map['mfa_method'] = Variable(mfaMethod.value); + } + if (keepAliveInterval.present) { + map['keep_alive_interval'] = Variable(keepAliveInterval.value); + } + if (locationMfaMode.present) { + map['location_mfa_mode'] = Variable(locationMfaMode.value); + } + if (postureCheckRequired.present) { + map['posture_check_required'] = Variable( + postureCheckRequired.value, + ); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocationsCompanion(') + ..write('id: $id, ') + ..write('instance: $instance, ') + ..write('networkId: $networkId, ') + ..write('name: $name, ') + ..write('address: $address, ') + ..write('pubKey: $pubKey, ') + ..write('endpoint: $endpoint, ') + ..write('allowedIps: $allowedIps, ') + ..write('dns: $dns, ') + ..write('mfaEnabled: $mfaEnabled, ') + ..write('trafficMethod: $trafficMethod, ') + ..write('mfaMethod: $mfaMethod, ') + ..write('keepAliveInterval: $keepAliveInterval, ') + ..write('locationMfaMode: $locationMfaMode, ') + ..write('postureCheckRequired: $postureCheckRequired') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV4 extends GeneratedDatabase { + DatabaseAtV4(QueryExecutor e) : super(e); + late final DefguardInstances defguardInstances = DefguardInstances(this); + late final Locations locations = Locations(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + defguardInstances, + locations, + ]; + @override + int get schemaVersion => 4; +} diff --git a/client/test/drift/defguard/migration_test.dart b/client/test/drift/defguard/migration_test.dart new file mode 100644 index 0000000..6ab73b2 --- /dev/null +++ b/client/test/drift/defguard/migration_test.dart @@ -0,0 +1,79 @@ +// dart format width=80 +// ignore_for_file: unused_local_variable, unused_import +import 'package:drift/drift.dart'; +import 'package:drift_dev/api/migrations_native.dart'; +import 'package:mobile/data/db/database.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'generated/schema.dart'; + +import 'generated/schema_v1.dart' as v1; +import 'generated/schema_v2.dart' as v2; + +void main() { + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + late SchemaVerifier verifier; + + setUpAll(() { + verifier = SchemaVerifier(GeneratedHelper()); + }); + + group('simple database migrations', () { + // These simple tests verify all possible schema updates with a simple (no + // data) migration. This is a quick way to ensure that written database + // migrations properly alter the schema. + const versions = GeneratedHelper.versions; + for (final (i, fromVersion) in versions.indexed) { + group('from $fromVersion', () { + for (final toVersion in versions.skip(i + 1)) { + test('to $toVersion', () async { + final schema = await verifier.schemaAt(fromVersion); + final db = AppDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, toVersion); + await db.close(); + }); + } + }); + } + }); + + // The following template shows how to write tests ensuring your migrations + // preserve existing data. + // Testing this can be useful for migrations that change existing columns + // (e.g. by alterating their type or constraints). Migrations that only add + // tables or columns typically don't need these advanced tests. For more + // information, see https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity + // TODO: This generated template shows how these tests could be written. Adopt + // it to your own needs when testing migrations with data integrity. + test('migration from v1 to v2 does not corrupt data', () async { + // Add data to insert into the old database, and the expected rows after the + // migration. + // TODO: Fill these lists + final oldDefguardInstancesData = []; + final expectedNewDefguardInstancesData = []; + + final oldLocationsData = []; + final expectedNewLocationsData = []; + + await verifier.testWithDataIntegrity( + oldVersion: 1, + newVersion: 2, + createOld: v1.DatabaseAtV1.new, + createNew: v2.DatabaseAtV2.new, + openTestedDatabase: AppDatabase.new, + createItems: (batch, oldDb) { + batch.insertAll(oldDb.defguardInstances, oldDefguardInstancesData); + batch.insertAll(oldDb.locations, oldLocationsData); + }, + validateItems: (newDb) async { + expect( + expectedNewDefguardInstancesData, + await newDb.select(newDb.defguardInstances).get(), + ); + expect( + expectedNewLocationsData, + await newDb.select(newDb.locations).get(), + ); + }, + ); + }); +} diff --git a/flake.lock b/flake.lock index 5ca09a9..333e23c 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1750776420, - "narHash": "sha256-/CG+w0o0oJ5itVklOoLbdn2dGB0wbZVOoDm4np6w09A=", + "lastModified": 1763421233, + "narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "30a61f056ac492e3b7cdcb69c1e6abdcf00e39cf", + "rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 9d34875..c2ca9d3 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,7 @@ allowUnfree = true; }; }; - ndkVersion = "27.0.12077973"; + ndkVersion = "28.2.13676358"; androidComposition = pkgs.androidenv.composeAndroidPackages { # buildToolsVersions = [ buildToolsVersion "28.0.3" ]; # platformVersions = [ "34" "28" ]; @@ -68,7 +68,7 @@ ''; in { devShell = with pkgs; - mkShell rec { + mkShell { ANDROID_SDK_ROOT = "${androidSdk}/libexec/android-sdk"; buildInputs = [ flutter @@ -85,6 +85,7 @@ export GDK_BACKEND=x11 export LANG=en_US.UTF-8 export QT_QPA_PLATFORM=xcb + export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath (with pkgs; [ sqlite ])}:$LD_LIBRARY_PATH"; ''; }; }); diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..75ec2ad --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["github>DefGuard/ci-workflows//renovate/default.json"], + "baseBranches": ["dev", "main"] +}