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
-
## 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