mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-05 23:53:21 +00:00
Compare commits
44 Commits
sugi
...
renovate/n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9356a901a | ||
|
|
2ad35be82e | ||
|
|
bdfc8ae5eb | ||
|
|
7d218eac2f | ||
|
|
ccbb24d504 | ||
|
|
dd1f36e3d6 | ||
|
|
0780f9a931 | ||
|
|
62c8a64298 | ||
|
|
0d3bbc1db8 | ||
|
|
99655a0abf | ||
|
|
2883ff6068 | ||
|
|
f5abaf114a | ||
|
|
5792578946 | ||
|
|
6b3b29a1a0 | ||
|
|
02809be178 | ||
|
|
6abfdd8a88 | ||
|
|
b95f57c4e7 | ||
|
|
9ecfc29ae4 | ||
|
|
e32f29b8e7 | ||
|
|
e333db372d | ||
|
|
a44eb28be8 | ||
|
|
ab436551de | ||
|
|
10e17adfb2 | ||
|
|
c7db8376ec | ||
|
|
bc996d680f | ||
|
|
fe01b49df1 | ||
|
|
daeb96713f | ||
|
|
f6791dabef | ||
|
|
a3a5ed8531 | ||
|
|
d3d62c30aa | ||
|
|
f81155b6b3 | ||
|
|
57a3ef04cc | ||
|
|
4e21b28276 | ||
|
|
1c2a0c677b | ||
|
|
5666f09e89 | ||
|
|
b13895bdd6 | ||
|
|
29fc4ad61e | ||
|
|
f722196149 | ||
|
|
a4ec6df118 | ||
|
|
01e60bf090 | ||
|
|
7c27202dab | ||
|
|
77ea7a395d | ||
|
|
a259de8b26 | ||
|
|
06dbc14136 |
203
.claude/CLAUDE.md
Normal file
203
.claude/CLAUDE.md
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# Bitwarden Directory Connector
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Directory Connector is a TypeScript application that synchronizes users and groups from directory services to Bitwarden organizations. It provides both a desktop GUI (built with Angular and Electron) and a CLI tool (bwdc).
|
||||||
|
|
||||||
|
**Supported Directory Services:**
|
||||||
|
|
||||||
|
- LDAP (Lightweight Directory Access Protocol) - includes Active Directory and general LDAP servers
|
||||||
|
- Microsoft Entra ID (formerly Azure Active Directory)
|
||||||
|
- Google Workspace
|
||||||
|
- Okta
|
||||||
|
- OneLogin
|
||||||
|
|
||||||
|
**Technologies:**
|
||||||
|
|
||||||
|
- TypeScript
|
||||||
|
- Angular (GUI)
|
||||||
|
- Electron (Desktop wrapper)
|
||||||
|
- Node
|
||||||
|
- Jest for testing
|
||||||
|
|
||||||
|
## Code Architecture & Structure
|
||||||
|
|
||||||
|
### Directory Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── abstractions/ # Interface definitions (e.g., IDirectoryService)
|
||||||
|
├── services/ # Business logic implementations for directory services, sync, auth
|
||||||
|
├── models/ # Data models (UserEntry, GroupEntry, etc.)
|
||||||
|
├── commands/ # CLI command implementations
|
||||||
|
├── app/ # Angular GUI components
|
||||||
|
└── utils/ # Test utilities and fixtures
|
||||||
|
|
||||||
|
src-cli/ # CLI-specific code (imports common code from src/)
|
||||||
|
|
||||||
|
jslib/ # Legacy folder structure (mix of deprecated/unused and current code - new code should not be added here)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Architectural Patterns
|
||||||
|
|
||||||
|
1. **Abstractions = Interfaces**: All interfaces are defined in `/abstractions`
|
||||||
|
2. **Services = Business Logic**: Implementations live in `/services`
|
||||||
|
3. **Directory Service Pattern**: Each directory provider implements `IDirectoryService` interface
|
||||||
|
4. **Separation of Concerns**: GUI (Angular app) and CLI (commands) share the same service layer
|
||||||
|
|
||||||
|
## Development Conventions
|
||||||
|
|
||||||
|
### Code Organization
|
||||||
|
|
||||||
|
**File Naming:**
|
||||||
|
|
||||||
|
- kebab-case for files: `ldap-directory.service.ts`
|
||||||
|
- Descriptive names that reflect purpose
|
||||||
|
|
||||||
|
**Class/Function Naming:**
|
||||||
|
|
||||||
|
- PascalCase for classes and interfaces
|
||||||
|
- camelCase for functions and variables
|
||||||
|
- Descriptive names that indicate purpose
|
||||||
|
|
||||||
|
**File Structure:**
|
||||||
|
|
||||||
|
- Keep files focused on single responsibility
|
||||||
|
- Create new service files for distinct directory integrations
|
||||||
|
- Separate models into individual files when complex
|
||||||
|
|
||||||
|
### TypeScript Conventions
|
||||||
|
|
||||||
|
**Import Patterns:**
|
||||||
|
|
||||||
|
- Use path aliases (`@/`) for project imports
|
||||||
|
- `@/` - project root
|
||||||
|
- `@/jslib/` - jslib folder
|
||||||
|
- ESLint enforces alphabetized import ordering with newlines between groups
|
||||||
|
|
||||||
|
**Type Safety:**
|
||||||
|
|
||||||
|
- Avoid `any` types - use proper typing or `unknown` with type guards
|
||||||
|
- Prefer interfaces for contracts, types for unions/intersections
|
||||||
|
- Use strict null checks - handle `null` and `undefined` explicitly
|
||||||
|
- Leverage TypeScript's type inference where appropriate
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
|
||||||
|
- Use configuration files or environment variables
|
||||||
|
- Never hardcode URLs or configuration values
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
**Credential Handling:**
|
||||||
|
|
||||||
|
- Never log directory service credentials, API keys, or tokens
|
||||||
|
- Use secure storage mechanisms for sensitive data
|
||||||
|
- Credentials should never be hardcoded
|
||||||
|
- Store credentials encrypted, never in plain text
|
||||||
|
|
||||||
|
**Sensitive Data:**
|
||||||
|
|
||||||
|
- User and group data from directories should be handled securely
|
||||||
|
- Avoid exposing sensitive information in error messages
|
||||||
|
- Sanitize data before logging
|
||||||
|
- Be cautious with data persistence
|
||||||
|
|
||||||
|
**Input Validation:**
|
||||||
|
|
||||||
|
- Validate and sanitize data from external directory services
|
||||||
|
- Check for injection vulnerabilities (LDAP injection, etc.)
|
||||||
|
- Validate configuration inputs from users
|
||||||
|
|
||||||
|
**API Security:**
|
||||||
|
|
||||||
|
- Ensure authentication flows are implemented correctly
|
||||||
|
- Verify SSL/TLS is used for all external connections
|
||||||
|
- Check for secure token storage and refresh mechanisms
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**Best Practices:**
|
||||||
|
|
||||||
|
1. **Try-catch for async operations** - Always wrap external API calls
|
||||||
|
2. **Meaningful error messages** - Provide context for debugging
|
||||||
|
3. **Error propagation** - Don't swallow errors silently
|
||||||
|
4. **User-facing errors** - Separate user messages from developer logs
|
||||||
|
|
||||||
|
## Performance Best Practices
|
||||||
|
|
||||||
|
**Large Dataset Handling:**
|
||||||
|
|
||||||
|
- Use pagination for large user/group lists
|
||||||
|
- Avoid loading entire datasets into memory at once
|
||||||
|
- Consider streaming or batch processing for large operations
|
||||||
|
|
||||||
|
**API Rate Limiting:**
|
||||||
|
|
||||||
|
- Respect rate limits for Microsoft Graph API, Google Admin SDK, etc.
|
||||||
|
- Consider batching large API calls where necessary
|
||||||
|
|
||||||
|
**Memory Management:**
|
||||||
|
|
||||||
|
- Close connections and clean up resources
|
||||||
|
- Remove event listeners when components are destroyed
|
||||||
|
- Be cautious with caching large datasets
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
**Framework:**
|
||||||
|
|
||||||
|
- Jest with jest-preset-angular
|
||||||
|
- jest-mock-extended for type-safe mocks with `mock<Type>()`
|
||||||
|
|
||||||
|
**Test Organization:**
|
||||||
|
|
||||||
|
- Tests colocated with source files
|
||||||
|
- `*.spec.ts` - Unit tests for individual components/services
|
||||||
|
- `*.integration.spec.ts` - Integration tests against live directory services
|
||||||
|
- Test helpers located in `utils/` directory
|
||||||
|
|
||||||
|
**Test Naming:**
|
||||||
|
|
||||||
|
- Descriptive, human-readable test names
|
||||||
|
- Example: `'should return empty array when no users exist in directory'`
|
||||||
|
|
||||||
|
**Test Coverage:**
|
||||||
|
|
||||||
|
- New features must include tests
|
||||||
|
- Bug fixes should include regression tests
|
||||||
|
- Changes to core sync logic or directory specific logic require integration tests
|
||||||
|
|
||||||
|
**Testing Approach:**
|
||||||
|
|
||||||
|
- **Unit tests**: Mock external API calls using jest-mock-extended
|
||||||
|
- **Integration tests**: Use live directory services (Docker containers or configured cloud services)
|
||||||
|
- Focus on critical paths (authentication, sync, data transformation)
|
||||||
|
- Test error scenarios and edge cases (empty results, malformed data, connection failures), not just happy paths
|
||||||
|
|
||||||
|
## Directory Service Patterns
|
||||||
|
|
||||||
|
### IDirectoryService Interface
|
||||||
|
|
||||||
|
All directory services implement this core interface with methods:
|
||||||
|
|
||||||
|
- `getUsers()` - Retrieve users from directory and transform them into standard objects
|
||||||
|
- `getGroups()` - Retrieve groups from directory and transform them into standard objects
|
||||||
|
- Connection and authentication handling
|
||||||
|
|
||||||
|
### Service-Specific Implementations
|
||||||
|
|
||||||
|
Each directory service has unique authentication and query patterns:
|
||||||
|
|
||||||
|
- **LDAP**: Direct LDAP queries, bind authentication
|
||||||
|
- **Microsoft Entra ID**: Microsoft Graph API, OAuth tokens
|
||||||
|
- **Google Workspace**: Google Admin SDK, service account credentials
|
||||||
|
- **Okta/OneLogin**: REST APIs with API tokens
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/)
|
||||||
|
- [Contributing Guidelines](https://contributing.bitwarden.com/contributing/)
|
||||||
|
- [Code Style](https://contributing.bitwarden.com/contributing/code-style/)
|
||||||
|
- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/)
|
||||||
|
- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions)
|
||||||
27
.claude/prompts/review-code.md
Normal file
27
.claude/prompts/review-code.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Please review this pull request with a focus on:
|
||||||
|
|
||||||
|
- Code quality and best practices
|
||||||
|
- Potential bugs or issues
|
||||||
|
- Security implications
|
||||||
|
- Performance considerations
|
||||||
|
|
||||||
|
Note: The PR branch is already checked out in the current working directory.
|
||||||
|
|
||||||
|
Provide a comprehensive review including:
|
||||||
|
|
||||||
|
- Summary of changes since last review
|
||||||
|
- Critical issues found (be thorough)
|
||||||
|
- Suggested improvements (be thorough)
|
||||||
|
- Good practices observed (be concise - list only the most notable items without elaboration)
|
||||||
|
- Action items for the author
|
||||||
|
- Leverage collapsible <details> sections where appropriate for lengthy explanations or code
|
||||||
|
snippets to enhance human readability
|
||||||
|
|
||||||
|
When reviewing subsequent commits:
|
||||||
|
|
||||||
|
- Track status of previously identified issues (fixed/unfixed/reopened)
|
||||||
|
- Identify NEW problems introduced since last review
|
||||||
|
- Note if fixes introduced new issues
|
||||||
|
|
||||||
|
IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note
|
||||||
|
what was done well without explaining why or praising excessively.
|
||||||
11
.github/CODEOWNERS
vendored
11
.github/CODEOWNERS
vendored
@@ -6,3 +6,14 @@
|
|||||||
|
|
||||||
# Default file owners.
|
# Default file owners.
|
||||||
* @bitwarden/team-admin-console-dev
|
* @bitwarden/team-admin-console-dev
|
||||||
|
|
||||||
|
# Docker-related files
|
||||||
|
**/Dockerfile @bitwarden/team-appsec @bitwarden/dept-bre
|
||||||
|
**/*.dockerignore @bitwarden/team-appsec @bitwarden/dept-bre
|
||||||
|
**/entrypoint.sh @bitwarden/team-appsec @bitwarden/dept-bre
|
||||||
|
**/docker-compose.yml @bitwarden/team-appsec @bitwarden/dept-bre
|
||||||
|
|
||||||
|
# Claude related files
|
||||||
|
.claude/ @bitwarden/team-ai-sme
|
||||||
|
.github/workflows/respond.yml @bitwarden/team-ai-sme
|
||||||
|
.github/workflows/review-code.yml @bitwarden/team-ai-sme
|
||||||
|
|||||||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Feature Requests
|
||||||
|
url: https://community.bitwarden.com/c/feature-requests/
|
||||||
|
about: Request new features using the Community Forums. Please search existing feature requests before making a new one.
|
||||||
|
- name: Bitwarden Community Forums
|
||||||
|
url: https://community.bitwarden.com
|
||||||
|
about: Please visit the community forums for general community discussion, support and the development roadmap.
|
||||||
|
- name: Customer Support
|
||||||
|
url: https://bitwarden.com/contact/
|
||||||
|
about: Please contact our customer support for account issues and general customer support.
|
||||||
|
- name: Security Issues
|
||||||
|
url: https://hackerone.com/bitwarden
|
||||||
|
about: We use HackerOne to manage security disclosures.
|
||||||
111
.github/ISSUE_TEMPLATE/issue.yml
vendored
Normal file
111
.github/ISSUE_TEMPLATE/issue.yml
vendored
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
name: Directory Connector Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
title: "[DC] "
|
||||||
|
labels: ["bug"]
|
||||||
|
type: bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
|
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
|
||||||
|
- type: textarea
|
||||||
|
id: reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps To Reproduce
|
||||||
|
description: How can we reproduce the behavior.
|
||||||
|
value: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. Click on '...'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected Result
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Actual Result
|
||||||
|
description: A clear and concise description of what is happening.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: screenshots
|
||||||
|
attributes:
|
||||||
|
label: Screenshots or Videos
|
||||||
|
description: If applicable, add screenshots and/or a short video to help explain your problem.
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context about the problem here.
|
||||||
|
- type: dropdown
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
description: What operating system(s) are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Windows
|
||||||
|
- macOS
|
||||||
|
- Linux
|
||||||
|
- Other operating system (please specify in "Additional Context" section)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: os-version
|
||||||
|
attributes:
|
||||||
|
label: Operating System Version
|
||||||
|
description: What version of the operating system(s) are you seeing the problem on?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: directories
|
||||||
|
attributes:
|
||||||
|
label: Directory Service
|
||||||
|
description: What directory service(s) are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- LDAP - Active Directory
|
||||||
|
- Another LDAP implementation (please specify in "Additional Context" section)
|
||||||
|
- Microsoft Entra ID
|
||||||
|
- Google Workspace
|
||||||
|
- Okta Universal Directory
|
||||||
|
- OneLogin
|
||||||
|
- Other directory service (please specify in "Additional Context" section)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: application-type
|
||||||
|
attributes:
|
||||||
|
label: Application Type
|
||||||
|
description: Which Directory Connector application(s) are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- GUI (the desktop application)
|
||||||
|
- CLI (the bwdc command line application)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Build Version
|
||||||
|
description: What version of our software are you running?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: issue-tracking-info
|
||||||
|
attributes:
|
||||||
|
label: Issue Tracking Info
|
||||||
|
description: |
|
||||||
|
Make sure to acknowledge the following before submitting your report!
|
||||||
|
options:
|
||||||
|
- label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
|
||||||
|
required: true
|
||||||
11
.github/renovate.json5
vendored
11
.github/renovate.json5
vendored
@@ -8,12 +8,6 @@
|
|||||||
matchManagers: ["github-actions"],
|
matchManagers: ["github-actions"],
|
||||||
matchUpdateTypes: ["minor", "patch"],
|
matchUpdateTypes: ["minor", "patch"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
groupName: "Google Libraries",
|
|
||||||
matchPackagePatterns: ["google-auth-library", "googleapis"],
|
|
||||||
matchManagers: ["npm"],
|
|
||||||
groupSlug: "google-libraries",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
ignoreDeps: [
|
ignoreDeps: [
|
||||||
// yao-pkg is used to create a single executable application bundle for the CLI.
|
// yao-pkg is used to create a single executable application bundle for the CLI.
|
||||||
@@ -21,5 +15,10 @@
|
|||||||
// This must be manually vetted by our appsec team before upgrading.
|
// This must be manually vetted by our appsec team before upgrading.
|
||||||
// It is excluded from renovate to avoid accidentally upgrading to a non-vetted version.
|
// It is excluded from renovate to avoid accidentally upgrading to a non-vetted version.
|
||||||
"@yao-pkg/pkg",
|
"@yao-pkg/pkg",
|
||||||
|
// googleapis uses ESM after 149.0.0 so we are not upgrading it until we have ESM support.
|
||||||
|
// They release new versions every couple of weeks so ignoring it at the dependency dashboard
|
||||||
|
// level is not sufficient.
|
||||||
|
// FIXME: remove and upgrade when we have ESM support.
|
||||||
|
"googleapis",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
126
.github/workflows/build.yml
vendored
126
.github/workflows/build.yml
vendored
@@ -23,20 +23,22 @@ jobs:
|
|||||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Get Package Version
|
- name: Get Package Version
|
||||||
id: retrieve-version
|
id: retrieve-version
|
||||||
run: |
|
run: |
|
||||||
PKG_VERSION=$(jq -r .version package.json)
|
PKG_VERSION=$(jq -r .version package.json)
|
||||||
echo "package_version=$PKG_VERSION" >> $GITHUB_OUTPUT
|
echo "package_version=$PKG_VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Get Node Version
|
- name: Get Node Version
|
||||||
id: retrieve-node-version
|
id: retrieve-node-version
|
||||||
run: |
|
run: |
|
||||||
NODE_NVMRC=$(cat .nvmrc)
|
NODE_NVMRC=$(cat .nvmrc)
|
||||||
NODE_VERSION=${NODE_NVMRC/v/''}
|
NODE_VERSION=${NODE_NVMRC/v/''}
|
||||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
echo "node_version=$NODE_VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
linux-cli:
|
linux-cli:
|
||||||
name: Build Linux CLI
|
name: Build Linux CLI
|
||||||
@@ -49,10 +51,12 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -61,7 +65,7 @@ jobs:
|
|||||||
- name: Update NPM
|
- name: Update NPM
|
||||||
run: |
|
run: |
|
||||||
npm install -g node-gyp
|
npm install -g node-gyp
|
||||||
node-gyp install $(node -v)
|
node-gyp install "$(node -v)"
|
||||||
|
|
||||||
- name: Keytar
|
- name: Keytar
|
||||||
run: |
|
run: |
|
||||||
@@ -72,8 +76,8 @@ jobs:
|
|||||||
keytarUrl="https://github.com/atom/node-keytar/releases/download/v$keytarVersion/$keytarTarGz"
|
keytarUrl="https://github.com/atom/node-keytar/releases/download/v$keytarVersion/$keytarTarGz"
|
||||||
|
|
||||||
mkdir -p ./keytar/linux
|
mkdir -p ./keytar/linux
|
||||||
wget $keytarUrl -O ./keytar/linux/$keytarTarGz
|
wget "$keytarUrl" -O "./keytar/linux/$keytarTarGz"
|
||||||
tar -xvf ./keytar/linux/$keytarTarGz -C ./keytar/linux
|
tar -xvf "./keytar/linux/$keytarTarGz" -C ./keytar/linux
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install
|
run: npm install
|
||||||
@@ -82,19 +86,19 @@ jobs:
|
|||||||
run: npm run dist:cli:lin
|
run: npm run dist:cli:lin
|
||||||
|
|
||||||
- name: Zip
|
- name: Zip
|
||||||
run: zip -j dist-cli/bwdc-linux-$_PACKAGE_VERSION.zip dist-cli/linux/bwdc keytar/linux/build/Release/keytar.node
|
run: zip -j "dist-cli/bwdc-linux-$_PACKAGE_VERSION.zip" "dist-cli/linux/bwdc" "keytar/linux/build/Release/keytar.node"
|
||||||
|
|
||||||
- name: Version Test
|
- name: Version Test
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt install libsecret-1-0 dbus-x11 gnome-keyring
|
sudo apt install libsecret-1-0 dbus-x11 gnome-keyring
|
||||||
eval $(dbus-launch --sh-syntax)
|
eval "$(dbus-launch --sh-syntax)"
|
||||||
|
|
||||||
eval $(echo -n "" | /usr/bin/gnome-keyring-daemon --login)
|
eval "$(echo -n "" | /usr/bin/gnome-keyring-daemon --login)"
|
||||||
eval $(/usr/bin/gnome-keyring-daemon --components=secrets --start)
|
eval "$(/usr/bin/gnome-keyring-daemon --components=secrets --start)"
|
||||||
|
|
||||||
mkdir -p test/linux
|
mkdir -p test/linux
|
||||||
unzip ./dist-cli/bwdc-linux-$_PACKAGE_VERSION.zip -d ./test/linux
|
unzip "./dist-cli/bwdc-linux-$_PACKAGE_VERSION.zip" -d ./test/linux
|
||||||
|
|
||||||
testVersion=$(./test/linux/bwdc -v)
|
testVersion=$(./test/linux/bwdc -v)
|
||||||
|
|
||||||
@@ -107,7 +111,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload Linux Zip to GitHub
|
- name: Upload Linux Zip to GitHub
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: bwdc-linux-${{ env._PACKAGE_VERSION }}.zip
|
name: bwdc-linux-${{ env._PACKAGE_VERSION }}.zip
|
||||||
path: ./dist-cli/bwdc-linux-${{ env._PACKAGE_VERSION }}.zip
|
path: ./dist-cli/bwdc-linux-${{ env._PACKAGE_VERSION }}.zip
|
||||||
@@ -116,7 +120,7 @@ jobs:
|
|||||||
|
|
||||||
macos-cli:
|
macos-cli:
|
||||||
name: Build Mac CLI
|
name: Build Mac CLI
|
||||||
runs-on: macos-13
|
runs-on: macos-15-intel
|
||||||
needs: setup
|
needs: setup
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -125,10 +129,12 @@ jobs:
|
|||||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -137,7 +143,7 @@ jobs:
|
|||||||
- name: Update NPM
|
- name: Update NPM
|
||||||
run: |
|
run: |
|
||||||
npm install -g node-gyp
|
npm install -g node-gyp
|
||||||
node-gyp install $(node -v)
|
node-gyp install "$(node -v)"
|
||||||
|
|
||||||
- name: Keytar
|
- name: Keytar
|
||||||
run: |
|
run: |
|
||||||
@@ -148,8 +154,8 @@ jobs:
|
|||||||
keytarUrl="https://github.com/atom/node-keytar/releases/download/v$keytarVersion/$keytarTarGz"
|
keytarUrl="https://github.com/atom/node-keytar/releases/download/v$keytarVersion/$keytarTarGz"
|
||||||
|
|
||||||
mkdir -p ./keytar/macos
|
mkdir -p ./keytar/macos
|
||||||
wget $keytarUrl -O ./keytar/macos/$keytarTarGz
|
wget "$keytarUrl" -O "./keytar/macos/$keytarTarGz"
|
||||||
tar -xvf ./keytar/macos/$keytarTarGz -C ./keytar/macos
|
tar -xvf "./keytar/macos/$keytarTarGz" -C ./keytar/macos
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install
|
run: npm install
|
||||||
@@ -158,12 +164,12 @@ jobs:
|
|||||||
run: npm run dist:cli:mac
|
run: npm run dist:cli:mac
|
||||||
|
|
||||||
- name: Zip
|
- name: Zip
|
||||||
run: zip -j dist-cli/bwdc-macos-$_PACKAGE_VERSION.zip dist-cli/macos/bwdc keytar/macos/build/Release/keytar.node
|
run: zip -j "dist-cli/bwdc-macos-$_PACKAGE_VERSION.zip" "dist-cli/macos/bwdc" "keytar/macos/build/Release/keytar.node"
|
||||||
|
|
||||||
- name: Version Test
|
- name: Version Test
|
||||||
run: |
|
run: |
|
||||||
mkdir -p test/macos
|
mkdir -p test/macos
|
||||||
unzip ./dist-cli/bwdc-macos-$_PACKAGE_VERSION.zip -d ./test/macos
|
unzip "./dist-cli/bwdc-macos-$_PACKAGE_VERSION.zip" -d ./test/macos
|
||||||
|
|
||||||
testVersion=$(./test/macos/bwdc -v)
|
testVersion=$(./test/macos/bwdc -v)
|
||||||
|
|
||||||
@@ -176,7 +182,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload Mac Zip to GitHub
|
- name: Upload Mac Zip to GitHub
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: bwdc-macos-${{ env._PACKAGE_VERSION }}.zip
|
name: bwdc-macos-${{ env._PACKAGE_VERSION }}.zip
|
||||||
path: ./dist-cli/bwdc-macos-${{ env._PACKAGE_VERSION }}.zip
|
path: ./dist-cli/bwdc-macos-${{ env._PACKAGE_VERSION }}.zip
|
||||||
@@ -194,14 +200,16 @@ jobs:
|
|||||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Windows builder
|
- name: Setup Windows builder
|
||||||
run: |
|
run: |
|
||||||
choco install checksum --no-progress
|
choco install checksum --no-progress
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -241,7 +249,7 @@ jobs:
|
|||||||
- name: Version Test
|
- name: Version Test
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Expand-Archive -Path "dist-cli\bwdc-windows-${{ env._PACKAGE_VERSION }}.zip" -DestinationPath "test\windows"
|
Expand-Archive -Path "dist-cli\bwdc-windows-$env:_PACKAGE_VERSION.zip" -DestinationPath "test\windows"
|
||||||
$testVersion = Invoke-Expression '& .\test\windows\bwdc.exe -v'
|
$testVersion = Invoke-Expression '& .\test\windows\bwdc.exe -v'
|
||||||
echo "version: ${env:_PACKAGE_VERSION}"
|
echo "version: ${env:_PACKAGE_VERSION}"
|
||||||
echo "testVersion: $testVersion"
|
echo "testVersion: $testVersion"
|
||||||
@@ -250,7 +258,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
- name: Upload Windows Zip to GitHub
|
- name: Upload Windows Zip to GitHub
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: bwdc-windows-${{ env._PACKAGE_VERSION }}.zip
|
name: bwdc-windows-${{ env._PACKAGE_VERSION }}.zip
|
||||||
path: ./dist-cli/bwdc-windows-${{ env._PACKAGE_VERSION }}.zip
|
path: ./dist-cli/bwdc-windows-${{ env._PACKAGE_VERSION }}.zip
|
||||||
@@ -271,10 +279,12 @@ jobs:
|
|||||||
HUSKY: 0
|
HUSKY: 0
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -328,28 +338,28 @@ jobs:
|
|||||||
SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }}
|
SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }}
|
||||||
|
|
||||||
- name: Upload Portable Executable to GitHub
|
- name: Upload Portable Executable to GitHub
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-Portable-${{ env._PACKAGE_VERSION }}.exe
|
name: Bitwarden-Connector-Portable-${{ env._PACKAGE_VERSION }}.exe
|
||||||
path: ./dist/Bitwarden-Connector-Portable-${{ env._PACKAGE_VERSION }}.exe
|
path: ./dist/Bitwarden-Connector-Portable-${{ env._PACKAGE_VERSION }}.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Installer Executable to GitHub
|
- name: Upload Installer Executable to GitHub
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe
|
name: Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe
|
||||||
path: ./dist/Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe
|
path: ./dist/Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Installer Executable Blockmap to GitHub
|
- name: Upload Installer Executable Blockmap to GitHub
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe.blockmap
|
name: Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe.blockmap
|
||||||
path: ./dist/Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe.blockmap
|
path: ./dist/Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe.blockmap
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload latest auto-update artifact
|
- name: Upload latest auto-update artifact
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: latest.yml
|
name: latest.yml
|
||||||
path: ./dist/latest.yml
|
path: ./dist/latest.yml
|
||||||
@@ -369,10 +379,12 @@ jobs:
|
|||||||
HUSKY: 0
|
HUSKY: 0
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -381,7 +393,7 @@ jobs:
|
|||||||
- name: Update NPM
|
- name: Update NPM
|
||||||
run: |
|
run: |
|
||||||
npm install -g node-gyp
|
npm install -g node-gyp
|
||||||
node-gyp install $(node -v)
|
node-gyp install "$(node -v)"
|
||||||
|
|
||||||
- name: Set up environment
|
- name: Set up environment
|
||||||
run: |
|
run: |
|
||||||
@@ -399,14 +411,14 @@ jobs:
|
|||||||
run: npm run dist:lin
|
run: npm run dist:lin
|
||||||
|
|
||||||
- name: Upload AppImage
|
- name: Upload AppImage
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
|
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
|
||||||
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
|
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload latest auto-update artifact
|
- name: Upload latest auto-update artifact
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: latest-linux.yml
|
name: latest-linux.yml
|
||||||
path: ./dist/latest-linux.yml
|
path: ./dist/latest-linux.yml
|
||||||
@@ -415,7 +427,7 @@ jobs:
|
|||||||
|
|
||||||
macos-gui:
|
macos-gui:
|
||||||
name: Build MacOS GUI
|
name: Build MacOS GUI
|
||||||
runs-on: macos-13
|
runs-on: macos-15-intel
|
||||||
needs: setup
|
needs: setup
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -427,10 +439,12 @@ jobs:
|
|||||||
HUSKY: 0
|
HUSKY: 0
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -439,7 +453,7 @@ jobs:
|
|||||||
- name: Update NPM
|
- name: Update NPM
|
||||||
run: |
|
run: |
|
||||||
npm install -g node-gyp
|
npm install -g node-gyp
|
||||||
node-gyp install $(node -v)
|
node-gyp install "$(node -v)"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -464,16 +478,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Get certificates
|
- name: Get certificates
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/certificates
|
mkdir -p "$HOME/certificates"
|
||||||
|
|
||||||
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-app-cert |
|
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-app-cert |
|
||||||
jq -r .value | base64 -d > $HOME/certificates/devid-app-cert.p12
|
jq -r .value | base64 -d > "$HOME/certificates/devid-app-cert.p12"
|
||||||
|
|
||||||
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-installer-cert |
|
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-installer-cert |
|
||||||
jq -r .value | base64 -d > $HOME/certificates/devid-installer-cert.p12
|
jq -r .value | base64 -d > "$HOME/certificates/devid-installer-cert.p12"
|
||||||
|
|
||||||
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/macdev-cert |
|
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/macdev-cert |
|
||||||
jq -r .value | base64 -d > $HOME/certificates/macdev-cert.p12
|
jq -r .value | base64 -d > "$HOME/certificates/macdev-cert.p12"
|
||||||
|
|
||||||
- name: Log out from Azure
|
- name: Log out from Azure
|
||||||
uses: bitwarden/gh-actions/azure-logout@main
|
uses: bitwarden/gh-actions/azure-logout@main
|
||||||
@@ -482,9 +496,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.KEYCHAIN-PASSWORD }}
|
KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.KEYCHAIN-PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
||||||
security default-keychain -s build.keychain
|
security default-keychain -s build.keychain
|
||||||
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
||||||
security set-keychain-settings -lut 1200 build.keychain
|
security set-keychain-settings -lut 1200 build.keychain
|
||||||
|
|
||||||
security import "$HOME/certificates/devid-app-cert.p12" -k build.keychain -P "" \
|
security import "$HOME/certificates/devid-app-cert.p12" -k build.keychain -P "" \
|
||||||
@@ -496,12 +510,12 @@ jobs:
|
|||||||
security import "$HOME/certificates/macdev-cert.p12" -k build.keychain -P "" \
|
security import "$HOME/certificates/macdev-cert.p12" -k build.keychain -P "" \
|
||||||
-T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
|
-T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
|
||||||
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
|
||||||
|
|
||||||
- name: Load package version
|
- name: Load package version
|
||||||
run: |
|
run: |
|
||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
$rootPath = $env:GITHUB_WORKSPACE;
|
||||||
$packageVersion = (Get-Content -Raw -Path $rootPath\package.json | ConvertFrom-Json).version;
|
$packageVersion = (Get-Content -Raw -Path "$rootPath\package.json" | ConvertFrom-Json).version;
|
||||||
|
|
||||||
Write-Output "Setting package version to $packageVersion";
|
Write-Output "Setting package version to $packageVersion";
|
||||||
Write-Output "PACKAGE_VERSION=$packageVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append;
|
Write-Output "PACKAGE_VERSION=$packageVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append;
|
||||||
@@ -511,10 +525,12 @@ jobs:
|
|||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Set up private auth key
|
- name: Set up private auth key
|
||||||
|
env:
|
||||||
|
_APP_STORE_CONNECT_AUTH_KEY: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-AUTH-KEY }}
|
||||||
run: |
|
run: |
|
||||||
mkdir ~/private_keys
|
mkdir ~/private_keys
|
||||||
cat << EOF > ~/private_keys/AuthKey_UFD296548T.p8
|
cat << EOF > ~/private_keys/AuthKey_UFD296548T.p8
|
||||||
${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-AUTH-KEY }}
|
${_APP_STORE_CONNECT_AUTH_KEY}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
- name: Build application
|
- name: Build application
|
||||||
@@ -526,28 +542,28 @@ jobs:
|
|||||||
CSC_FOR_PULL_REQUEST: true
|
CSC_FOR_PULL_REQUEST: true
|
||||||
|
|
||||||
- name: Upload .zip artifact
|
- name: Upload .zip artifact
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-mac.zip
|
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-mac.zip
|
||||||
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-mac.zip
|
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-mac.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload .dmg artifact
|
- name: Upload .dmg artifact
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg
|
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg
|
||||||
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg
|
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload .dmg Blockmap artifact
|
- name: Upload .dmg Blockmap artifact
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg.blockmap
|
name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg.blockmap
|
||||||
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg.blockmap
|
path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg.blockmap
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload latest auto-update artifact
|
- name: Upload latest auto-update artifact
|
||||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: latest-mac.yml
|
name: latest-mac.yml
|
||||||
path: ./dist/latest-mac.yml
|
path: ./dist/latest-mac.yml
|
||||||
|
|||||||
109
.github/workflows/integration-test.yml
vendored
109
.github/workflows/integration-test.yml
vendored
@@ -2,25 +2,36 @@ name: Integration Testing
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
# Integration tests are slow, so only run them if relevant files have changed.
|
||||||
|
# This is done at the workflow level and at the job level.
|
||||||
|
# Make sure these triggers stay consistent with the 'changed-files' job.
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "main"
|
- 'main'
|
||||||
|
- 'rc'
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/integration-test.yml" # this file
|
- ".github/workflows/integration-test.yml" # this file
|
||||||
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
|
- "docker-compose.yml" # any change to Docker configuration
|
||||||
- "./openldap/**/*" # any change to test fixtures
|
- "package.json" # dependencies
|
||||||
- "./docker-compose.yml" # any change to Docker configuration
|
- "utils/**" # any change to test fixtures
|
||||||
- "./package.json" # dependencies
|
- "src/services/sync.service.ts" # core sync service used by all directory services
|
||||||
|
- "src/services/directory-services/ldap-directory.service*" # LDAP directory service
|
||||||
|
- "src/services/directory-services/gsuite-directory.service*" # Google Workspace directory service
|
||||||
|
# Add directory services here as we add test coverage
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/integration-test.yml" # this file
|
- ".github/workflows/integration-test.yml" # this file
|
||||||
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
|
- "docker-compose.yml" # any change to Docker configuration
|
||||||
- "./openldap/**/*" # any change to test fixtures
|
- "package.json" # dependencies
|
||||||
- "./docker-compose.yml" # any change to Docker configuration
|
- "utils/**" # any change to test fixtures
|
||||||
- "./package.json" # dependencies
|
- "src/services/sync.service.ts" # core sync service used by all directory services
|
||||||
|
- "src/services/directory-services/ldap-directory.service*" # LDAP directory service
|
||||||
|
- "src/services/directory-services/gsuite-directory.service*" # Google Workspace directory service
|
||||||
|
# Add directory services here as we add test coverage
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
checks: write # required by dorny/test-reporter to upload its results
|
checks: write # required by dorny/test-reporter to upload its results
|
||||||
|
id-token: write # required to use OIDC to login to Azure Key Vault
|
||||||
jobs:
|
jobs:
|
||||||
testing:
|
testing:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
@@ -29,17 +40,19 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Get Node version
|
- name: Get Node version
|
||||||
id: retrieve-node-version
|
id: retrieve-node-version
|
||||||
run: |
|
run: |
|
||||||
NODE_NVMRC=$(cat .nvmrc)
|
NODE_NVMRC=$(cat .nvmrc)
|
||||||
NODE_VERSION=${NODE_NVMRC/v/''}
|
NODE_VERSION=${NODE_NVMRC/v/''}
|
||||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
echo "node_version=$NODE_VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -48,23 +61,81 @@ jobs:
|
|||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Install mkcert
|
# Get secrets from Azure Key Vault
|
||||||
|
- name: Azure Login
|
||||||
|
uses: bitwarden/gh-actions/azure-login@main
|
||||||
|
with:
|
||||||
|
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
|
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
client_id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
|
|
||||||
|
- name: Get KV Secrets
|
||||||
|
id: get-kv-secrets
|
||||||
|
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||||
|
with:
|
||||||
|
keyvault: gh-directory-connector
|
||||||
|
secrets: "GOOGLE-ADMIN-USER,GOOGLE-CLIENT-EMAIL,GOOGLE-DOMAIN,GOOGLE-PRIVATE-KEY"
|
||||||
|
|
||||||
|
- name: Azure Logout
|
||||||
|
uses: bitwarden/gh-actions/azure-logout@main
|
||||||
|
|
||||||
|
# Only run relevant tests depending on what files have changed.
|
||||||
|
# This should be kept consistent with the workflow level triggers.
|
||||||
|
# Note: docker-compose.yml is only used for ldap for now
|
||||||
|
- name: Get changed files
|
||||||
|
id: changed-files
|
||||||
|
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
|
with:
|
||||||
|
list-files: shell
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Add directory services here as we add test coverage
|
||||||
|
filters: |
|
||||||
|
common:
|
||||||
|
- '.github/workflows/integration-test.yml'
|
||||||
|
- 'utils/**'
|
||||||
|
- 'package.json'
|
||||||
|
- 'src/services/sync.service.ts'
|
||||||
|
ldap:
|
||||||
|
- 'docker-compose.yml'
|
||||||
|
- 'src/services/directory-services/ldap-directory.service*'
|
||||||
|
google:
|
||||||
|
- 'src/services/directory-services/gsuite-directory.service*'
|
||||||
|
|
||||||
|
# LDAP
|
||||||
|
- name: Setup LDAP integration tests
|
||||||
|
if: steps.changed-files.outputs.common == 'true' || steps.changed-files.outputs.ldap == 'true'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install mkcert
|
sudo apt-get -y install mkcert
|
||||||
|
npm run test:integration:setup
|
||||||
|
|
||||||
- name: Setup integration tests
|
- name: Run LDAP integration tests
|
||||||
run: npm run test:integration:setup
|
if: steps.changed-files.outputs.common == 'true' || steps.changed-files.outputs.ldap == 'true'
|
||||||
|
env:
|
||||||
|
JEST_JUNIT_UNIQUE_OUTPUT_NAME: "true" # avoids junit outputs from clashing
|
||||||
|
run: npx jest ldap-directory.service.integration.spec.ts --coverage --coverageDirectory=coverage-ldap
|
||||||
|
|
||||||
- name: Run integration tests
|
# Google Workspace
|
||||||
run: npm run test:integration --coverage
|
- name: Run Google Workspace integration tests
|
||||||
|
if: steps.changed-files.outputs.common == 'true' || steps.changed-files.outputs.google == 'true'
|
||||||
|
env:
|
||||||
|
GOOGLE_DOMAIN: ${{ steps.get-kv-secrets.outputs.GOOGLE-DOMAIN }}
|
||||||
|
GOOGLE_ADMIN_USER: ${{ steps.get-kv-secrets.outputs.GOOGLE-ADMIN-USER }}
|
||||||
|
GOOGLE_CLIENT_EMAIL: ${{ steps.get-kv-secrets.outputs.GOOGLE-CLIENT-EMAIL }}
|
||||||
|
GOOGLE_PRIVATE_KEY: ${{ steps.get-kv-secrets.outputs.GOOGLE-PRIVATE-KEY }}
|
||||||
|
JEST_JUNIT_UNIQUE_OUTPUT_NAME: "true" # avoids junit outputs from clashing
|
||||||
|
run: |
|
||||||
|
npx jest gsuite-directory.service.integration.spec.ts --coverage --coverageDirectory=coverage-google
|
||||||
|
|
||||||
- name: Report test results
|
- name: Report test results
|
||||||
|
id: report
|
||||||
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1
|
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1
|
||||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }}
|
# This will skip the job if it's a pull request from a fork, because that won't have permission to upload test results.
|
||||||
|
# PRs from the repository and all other events are OK.
|
||||||
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository) && !cancelled()
|
||||||
with:
|
with:
|
||||||
name: Test Results
|
name: Test Results
|
||||||
path: "junit.xml"
|
path: "junit.xml*"
|
||||||
reporter: jest-junit
|
reporter: jest-junit
|
||||||
fail-on-error: true
|
fail-on-error: true
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -26,7 +26,9 @@ jobs:
|
|||||||
release_version: ${{ steps.version.outputs.version }}
|
release_version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Branch check
|
- name: Branch check
|
||||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||||
|
|||||||
28
.github/workflows/respond.yml
vendored
Normal file
28
.github/workflows/respond.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Respond
|
||||||
|
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
pull_request_review_comment:
|
||||||
|
types: [created]
|
||||||
|
issues:
|
||||||
|
types: [opened, assigned]
|
||||||
|
pull_request_review:
|
||||||
|
types: [submitted]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
respond:
|
||||||
|
name: Respond
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/_respond.yml@main
|
||||||
|
secrets:
|
||||||
|
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
|
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
21
.github/workflows/review-code.yml
vendored
Normal file
21
.github/workflows/review-code.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Code Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
name: Review
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/_review-code.yml@main
|
||||||
|
secrets:
|
||||||
|
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
|
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
pull-requests: write
|
||||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -22,17 +22,19 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Get Node version
|
- name: Get Node version
|
||||||
id: retrieve-node-version
|
id: retrieve-node-version
|
||||||
run: |
|
run: |
|
||||||
NODE_NVMRC=$(cat .nvmrc)
|
NODE_NVMRC=$(cat .nvmrc)
|
||||||
NODE_VERSION=${NODE_NVMRC/v/''}
|
NODE_VERSION=${NODE_NVMRC/v/''}
|
||||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
echo "node_version=$NODE_VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
@@ -52,7 +54,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Report test results
|
- name: Report test results
|
||||||
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1
|
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1
|
||||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }}
|
# This will skip the job if it's a pull request from a fork, because that won't have permission to upload test results.
|
||||||
|
# PRs from the repository and all other events are OK.
|
||||||
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository) && !cancelled()
|
||||||
with:
|
with:
|
||||||
name: Test Results
|
name: Test Results
|
||||||
path: "junit.xml"
|
path: "junit.xml"
|
||||||
|
|||||||
30
.github/workflows/version-bump.yml
vendored
30
.github/workflows/version-bump.yml
vendored
@@ -49,9 +49,10 @@ jobs:
|
|||||||
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
|
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
|
||||||
|
|
||||||
- name: Checkout Branch
|
- name: Checkout Branch
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.app-token.outputs.token }}
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
persist-credentials: true
|
||||||
|
|
||||||
- name: Setup git
|
- name: Setup git
|
||||||
run: |
|
run: |
|
||||||
@@ -62,7 +63,7 @@ jobs:
|
|||||||
id: current-version
|
id: current-version
|
||||||
run: |
|
run: |
|
||||||
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
||||||
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
echo "version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Verify input version
|
- name: Verify input version
|
||||||
if: ${{ inputs.version_number_override != '' }}
|
if: ${{ inputs.version_number_override != '' }}
|
||||||
@@ -77,8 +78,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if version is newer.
|
# Check if version is newer.
|
||||||
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
|
if printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V; then
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Version check successful."
|
echo "Version check successful."
|
||||||
else
|
else
|
||||||
echo "Version check failed."
|
echo "Version check failed."
|
||||||
@@ -110,26 +110,34 @@ jobs:
|
|||||||
|
|
||||||
- name: Set final version output
|
- name: Set final version output
|
||||||
id: set-final-version-output
|
id: set-final-version-output
|
||||||
|
env:
|
||||||
|
_BUMP_VERSION_OVERRIDE_OUTCOME: ${{ steps.bump-version-override.outcome }}
|
||||||
|
_INPUT_VERSION_NUMBER_OVERRIDE: ${{ inputs.version_number_override }}
|
||||||
|
_BUMP_VERSION_AUTOMATIC_OUTCOME: ${{ steps.bump-version-automatic.outcome }}
|
||||||
|
_CALCULATE_NEXT_VERSION: ${{ steps.calculate-next-version.outputs.version }}
|
||||||
|
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ steps.bump-version-override.outcome }}" == "success" ]]; then
|
if [[ "$_BUMP_VERSION_OVERRIDE_OUTCOME" == "success" ]]; then
|
||||||
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
echo "version=$_INPUT_VERSION_NUMBER_OVERRIDE" >> "$GITHUB_OUTPUT"
|
||||||
elif [[ "${{ steps.bump-version-automatic.outcome }}" == "success" ]]; then
|
elif [[ "$_BUMP_VERSION_AUTOMATIC_OUTCOME" == "success" ]]; then
|
||||||
echo "version=${{ steps.calculate-next-version.outputs.version }}" >> $GITHUB_OUTPUT
|
echo "version=$_CALCULATE_NEXT_VERSION" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check if version changed
|
- name: Check if version changed
|
||||||
id: version-changed
|
id: version-changed
|
||||||
run: |
|
run: |
|
||||||
if [ -n "$(git status --porcelain)" ]; then
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT
|
echo "changes_to_commit=TRUE" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT
|
echo "changes_to_commit=FALSE" >> "$GITHUB_OUTPUT"
|
||||||
echo "No changes to commit!";
|
echo "No changes to commit!";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Commit files
|
- name: Commit files
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||||
run: git commit -m "Bumped version to ${{ steps.set-final-version-output.outputs.version }}" -a
|
env:
|
||||||
|
_VERSION: ${{ steps.set-final-version-output.outputs.version }}
|
||||||
|
run: git commit -m "Bumped version to $_VERSION" -a
|
||||||
|
|
||||||
- name: Push changes
|
- name: Push changes
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -2,6 +2,9 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Environment variables used for tests
|
||||||
|
.env
|
||||||
|
|
||||||
# IDEs and editors
|
# IDEs and editors
|
||||||
.idea/
|
.idea/
|
||||||
.project
|
.project
|
||||||
@@ -30,8 +33,8 @@ build-cli
|
|||||||
.angular/cache
|
.angular/cache
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
coverage
|
coverage*
|
||||||
junit.xml
|
junit.xml*
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
*.crx
|
*.crx
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
open-ldap:
|
open-ldap:
|
||||||
image: bitnami/openldap:latest
|
image: bitnamilegacy/openldap:latest
|
||||||
hostname: openldap
|
hostname: openldap
|
||||||
environment:
|
environment:
|
||||||
- LDAP_ADMIN_USERNAME=admin
|
- LDAP_ADMIN_USERNAME=admin
|
||||||
@@ -11,8 +11,8 @@ services:
|
|||||||
- LDAP_TLS_KEY_FILE=/certs/openldap-key.pem
|
- LDAP_TLS_KEY_FILE=/certs/openldap-key.pem
|
||||||
- LDAP_TLS_CA_FILE=/certs/rootCA.pem
|
- LDAP_TLS_CA_FILE=/certs/rootCA.pem
|
||||||
volumes:
|
volumes:
|
||||||
- "./openldap/ldifs:/ldifs"
|
- "./utils/openldap/ldifs:/ldifs"
|
||||||
- "./openldap/certs:/certs"
|
- "./utils/openldap/certs:/certs"
|
||||||
ports:
|
ports:
|
||||||
- "1389:1389"
|
- "1389:1389"
|
||||||
- "1636:1636"
|
- "1636:1636"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { InjectFlags, InjectOptions, Injector, ProviderToken } from "@angular/core";
|
import { InjectOptions, Injector, ProviderToken } from "@angular/core";
|
||||||
|
|
||||||
export class ModalInjector implements Injector {
|
export class ModalInjector implements Injector {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -12,8 +12,7 @@ export class ModalInjector implements Injector {
|
|||||||
options: InjectOptions & { optional?: false },
|
options: InjectOptions & { optional?: false },
|
||||||
): T;
|
): T;
|
||||||
get<T>(token: ProviderToken<T>, notFoundValue: null, options: InjectOptions): T;
|
get<T>(token: ProviderToken<T>, notFoundValue: null, options: InjectOptions): T;
|
||||||
get<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions | InjectFlags): T;
|
get<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
|
||||||
get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
|
|
||||||
get(token: any, notFoundValue?: any): any;
|
get(token: any, notFoundValue?: any): any;
|
||||||
get(token: any, notFoundValue?: any, flags?: any): any {
|
get(token: any, notFoundValue?: any, flags?: any): any {
|
||||||
return this._additionalTokens.get(token) ?? this._parentInjector.get<any>(token, notFoundValue);
|
return this._additionalTokens.get(token) ?? this._parentInjector.get<any>(token, notFoundValue);
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
if ! [ -x "$(command -v mkcert)" ]; then
|
|
||||||
echo 'Error: mkcert is not installed. Install mkcert first and then re-run this script.'
|
|
||||||
echo 'e.g. brew install mkcert'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkcert -install
|
|
||||||
mkdir -p ./openldap/certs
|
|
||||||
cp "$(mkcert -CAROOT)/rootCA.pem" ./openldap/certs/rootCA.pem
|
|
||||||
mkcert -key-file ./openldap/certs/openldap-key.pem -cert-file ./openldap/certs/openldap.pem localhost openldap
|
|
||||||
11204
package-lock.json
generated
11204
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
65
package.json
65
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "@bitwarden/directory-connector",
|
"name": "@bitwarden/directory-connector",
|
||||||
"productName": "Bitwarden Directory Connector",
|
"productName": "Bitwarden Directory Connector",
|
||||||
"description": "Sync your user directory to your Bitwarden organization.",
|
"description": "Sync your user directory to your Bitwarden organization.",
|
||||||
"version": "2025.9.0",
|
"version": "2025.11.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bitwarden",
|
"bitwarden",
|
||||||
"password",
|
"password",
|
||||||
@@ -69,19 +69,19 @@
|
|||||||
"test:watch:all": "jest --watchAll --testPathIgnorePatterns=.integration.spec.ts",
|
"test:watch:all": "jest --watchAll --testPathIgnorePatterns=.integration.spec.ts",
|
||||||
"test:integration": "jest .integration.spec.ts",
|
"test:integration": "jest .integration.spec.ts",
|
||||||
"test:integration:watch": "jest .integration.spec.ts --watch",
|
"test:integration:watch": "jest .integration.spec.ts --watch",
|
||||||
"test:integration:setup": "sh ./openldap/mkcert.sh && docker compose up -d",
|
"test:integration:setup": "sh ./utils/openldap/mkcert.sh && docker compose up -d",
|
||||||
"test:types": "npx tsc --noEmit"
|
"test:types": "npx tsc --noEmit"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "19.2.15",
|
"@angular-devkit/build-angular": "20.3.3",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.8.0",
|
"@angular-eslint/eslint-plugin-template": "20.6.0",
|
||||||
"@angular-eslint/template-parser": "19.8.0",
|
"@angular-eslint/template-parser": "20.6.0",
|
||||||
"@angular/compiler-cli": "19.2.14",
|
"@angular/compiler-cli": "20.3.15",
|
||||||
"@electron/notarize": "2.5.0",
|
"@electron/notarize": "2.5.0",
|
||||||
"@electron/rebuild": "4.0.1",
|
"@electron/rebuild": "4.0.1",
|
||||||
"@fluffy-spoon/substitute": "1.208.0",
|
"@fluffy-spoon/substitute": "1.208.0",
|
||||||
"@microsoft/microsoft-graph-types": "2.40.0",
|
"@microsoft/microsoft-graph-types": "2.43.1",
|
||||||
"@ngtools/webpack": "19.2.14",
|
"@ngtools/webpack": "20.3.3",
|
||||||
"@types/inquirer": "8.2.10",
|
"@types/inquirer": "8.2.10",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
"@types/lowdb": "1.0.15",
|
"@types/lowdb": "1.0.15",
|
||||||
@@ -90,8 +90,8 @@
|
|||||||
"@types/node-forge": "1.3.11",
|
"@types/node-forge": "1.3.11",
|
||||||
"@types/proper-lockfile": "4.1.4",
|
"@types/proper-lockfile": "4.1.4",
|
||||||
"@types/tldjs": "2.3.4",
|
"@types/tldjs": "2.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "8.43.0",
|
"@typescript-eslint/eslint-plugin": "8.46.0",
|
||||||
"@typescript-eslint/parser": "8.43.0",
|
"@typescript-eslint/parser": "8.46.0",
|
||||||
"@yao-pkg/pkg": "5.16.1",
|
"@yao-pkg/pkg": "5.16.1",
|
||||||
"clean-webpack-plugin": "4.0.0",
|
"clean-webpack-plugin": "4.0.0",
|
||||||
"concurrently": "9.2.0",
|
"concurrently": "9.2.0",
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"dotenv": "17.2.0",
|
"dotenv": "17.2.0",
|
||||||
"electron": "38.1.0",
|
"electron": "39.2.1",
|
||||||
"electron-builder": "24.13.3",
|
"electron-builder": "24.13.3",
|
||||||
"electron-log": "5.4.1",
|
"electron-log": "5.4.1",
|
||||||
"electron-reload": "2.0.0-alpha.1",
|
"electron-reload": "2.0.0-alpha.1",
|
||||||
@@ -112,6 +112,7 @@
|
|||||||
"eslint-plugin-rxjs": "5.0.3",
|
"eslint-plugin-rxjs": "5.0.3",
|
||||||
"eslint-plugin-rxjs-angular": "2.0.1",
|
"eslint-plugin-rxjs-angular": "2.0.1",
|
||||||
"form-data": "4.0.4",
|
"form-data": "4.0.4",
|
||||||
|
"glob": "11.1.0",
|
||||||
"html-loader": "5.1.0",
|
"html-loader": "5.1.0",
|
||||||
"html-webpack-plugin": "5.6.3",
|
"html-webpack-plugin": "5.6.3",
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
@@ -119,38 +120,38 @@
|
|||||||
"jest-junit": "16.0.0",
|
"jest-junit": "16.0.0",
|
||||||
"jest-mock-extended": "3.0.7",
|
"jest-mock-extended": "3.0.7",
|
||||||
"jest-preset-angular": "14.6.0",
|
"jest-preset-angular": "14.6.0",
|
||||||
"lint-staged": "16.1.2",
|
"lint-staged": "16.2.6",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"node-abi": "3.77.0",
|
"minimatch": "5.1.2",
|
||||||
"node-forge": "1.3.1",
|
"node-forge": "1.3.2",
|
||||||
"node-loader": "2.1.0",
|
"node-loader": "2.1.0",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"rimraf": "6.0.1",
|
"rimraf": "6.1.0",
|
||||||
"rxjs": "7.8.2",
|
"rxjs": "7.8.2",
|
||||||
"sass": "1.92.1",
|
"sass": "1.93.2",
|
||||||
"sass-loader": "16.0.5",
|
"sass-loader": "16.0.5",
|
||||||
"ts-jest": "29.4.1",
|
"ts-jest": "29.4.1",
|
||||||
"ts-loader": "9.5.2",
|
"ts-loader": "9.5.2",
|
||||||
"tsconfig-paths-webpack-plugin": "4.2.0",
|
"tsconfig-paths-webpack-plugin": "4.2.0",
|
||||||
"type-fest": "4.41.0",
|
"type-fest": "5.0.1",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.8.3",
|
||||||
"webpack": "5.101.0",
|
"webpack": "5.102.1",
|
||||||
"webpack-cli": "6.0.1",
|
"webpack-cli": "6.0.1",
|
||||||
"webpack-merge": "6.0.1",
|
"webpack-merge": "6.0.1",
|
||||||
"webpack-node-externals": "3.0.0",
|
"webpack-node-externals": "3.0.0",
|
||||||
"zone.js": "0.15.1"
|
"zone.js": "0.15.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "19.2.14",
|
"@angular/animations": "20.3.15",
|
||||||
"@angular/cdk": "19.2.14",
|
"@angular/cdk": "20.2.14",
|
||||||
"@angular/cli": "19.2.14",
|
"@angular/cli": "20.3.3",
|
||||||
"@angular/common": "19.2.14",
|
"@angular/common": "20.3.15",
|
||||||
"@angular/compiler": "19.2.14",
|
"@angular/compiler": "20.3.15",
|
||||||
"@angular/core": "19.2.14",
|
"@angular/core": "20.3.15",
|
||||||
"@angular/forms": "19.2.14",
|
"@angular/forms": "20.3.15",
|
||||||
"@angular/platform-browser": "19.2.14",
|
"@angular/platform-browser": "20.3.15",
|
||||||
"@angular/platform-browser-dynamic": "19.2.14",
|
"@angular/platform-browser-dynamic": "20.3.15",
|
||||||
"@angular/router": "19.2.14",
|
"@angular/router": "20.3.15",
|
||||||
"@microsoft/microsoft-graph-client": "3.0.7",
|
"@microsoft/microsoft-graph-client": "3.0.7",
|
||||||
"big-integer": "1.6.52",
|
"big-integer": "1.6.52",
|
||||||
"bootstrap": "5.3.7",
|
"bootstrap": "5.3.7",
|
||||||
@@ -158,16 +159,14 @@
|
|||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"commander": "14.0.0",
|
"commander": "14.0.0",
|
||||||
"form-data": "4.0.4",
|
"form-data": "4.0.4",
|
||||||
"google-auth-library": "10.3.0",
|
"googleapis": "149.0.0",
|
||||||
"googleapis": "153.0.0",
|
|
||||||
"googleapis-common": "8.0.0",
|
|
||||||
"https-proxy-agent": "7.0.6",
|
"https-proxy-agent": "7.0.6",
|
||||||
"inquirer": "8.2.6",
|
"inquirer": "8.2.6",
|
||||||
"keytar": "7.9.0",
|
"keytar": "7.9.0",
|
||||||
"ldapts": "8.0.1",
|
"ldapts": "8.0.1",
|
||||||
"lowdb": "1.0.0",
|
"lowdb": "1.0.0",
|
||||||
"ngx-toastr": "19.0.0",
|
"ngx-toastr": "19.1.0",
|
||||||
"node-fetch": "2.7.0",
|
"node-fetch": "3.3.2",
|
||||||
"parse5": "8.0.0",
|
"parse5": "8.0.0",
|
||||||
"proper-lockfile": "4.1.2",
|
"proper-lockfile": "4.1.2",
|
||||||
"rxjs": "7.8.2",
|
"rxjs": "7.8.2",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DirectoryType } from "@/src/enums/directoryType";
|
import { DirectoryType } from "@/src/enums/directoryType";
|
||||||
import { IDirectoryService } from "@/src/services/directory.service";
|
import { IDirectoryService } from "@/src/services/directory-services/directory.service";
|
||||||
|
|
||||||
export abstract class DirectoryFactoryService {
|
export abstract class DirectoryFactoryService {
|
||||||
abstract createService(type: DirectoryType): IDirectoryService;
|
abstract createService(type: DirectoryType): IDirectoryService;
|
||||||
|
|||||||
@@ -768,5 +768,8 @@
|
|||||||
},
|
},
|
||||||
"launchWebVault": {
|
"launchWebVault": {
|
||||||
"message": "Launch Web Vault"
|
"message": "Launch Web Vault"
|
||||||
|
},
|
||||||
|
"authenticationFailed": {
|
||||||
|
"message": "Authentication failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { GetUniqueString } from "@/jslib/common/spec/utils";
|
|||||||
|
|
||||||
import { UserEntry } from "@/src/models/userEntry";
|
import { UserEntry } from "@/src/models/userEntry";
|
||||||
|
|
||||||
|
import { groupSimulator, userSimulator } from "../../utils/request-builder-helper";
|
||||||
import { RequestBuilderOptions } from "../abstractions/request-builder.service";
|
import { RequestBuilderOptions } from "../abstractions/request-builder.service";
|
||||||
import { groupSimulator, userSimulator } from "../utils/request-builder-helper";
|
|
||||||
|
|
||||||
import { BatchRequestBuilder } from "./batch-request-builder";
|
import { BatchRequestBuilder } from "./batch-request-builder";
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { DirectoryFactoryService } from "../abstractions/directory-factory.servi
|
|||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../abstractions/state.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../enums/directoryType";
|
||||||
|
|
||||||
import { EntraIdDirectoryService } from "./entra-id-directory.service";
|
import { EntraIdDirectoryService } from "./directory-services/entra-id-directory.service";
|
||||||
import { GSuiteDirectoryService } from "./gsuite-directory.service";
|
import { GSuiteDirectoryService } from "./directory-services/gsuite-directory.service";
|
||||||
import { LdapDirectoryService } from "./ldap-directory.service";
|
import { LdapDirectoryService } from "./directory-services/ldap-directory.service";
|
||||||
import { OktaDirectoryService } from "./okta-directory.service";
|
import { OktaDirectoryService } from "./directory-services/okta-directory.service";
|
||||||
import { OneLoginDirectoryService } from "./onelogin-directory.service";
|
import { OneLoginDirectoryService } from "./directory-services/onelogin-directory.service";
|
||||||
|
|
||||||
export class DefaultDirectoryFactoryService implements DirectoryFactoryService {
|
export class DefaultDirectoryFactoryService implements DirectoryFactoryService {
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../../models/groupEntry";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../../models/userEntry";
|
||||||
|
|
||||||
export interface IDirectoryService {
|
export interface IDirectoryService {
|
||||||
getEntries(force: boolean, test: boolean): Promise<[GroupEntry[], UserEntry[]]>;
|
getEntries(force: boolean, test: boolean): Promise<[GroupEntry[], UserEntry[]]>;
|
||||||
@@ -7,14 +7,14 @@ import * as graphType from "@microsoft/microsoft-graph-types";
|
|||||||
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
||||||
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
||||||
|
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../../abstractions/state.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
import { EntraIdConfiguration } from "../models/entraIdConfiguration";
|
import { EntraIdConfiguration } from "../../models/entraIdConfiguration";
|
||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../../models/groupEntry";
|
||||||
import { SyncConfiguration } from "../models/syncConfiguration";
|
import { SyncConfiguration } from "../../models/syncConfiguration";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../../models/userEntry";
|
||||||
|
import { BaseDirectoryService } from "../baseDirectory.service";
|
||||||
|
|
||||||
import { BaseDirectoryService } from "./baseDirectory.service";
|
|
||||||
import { IDirectoryService } from "./directory.service";
|
import { IDirectoryService } from "./directory.service";
|
||||||
|
|
||||||
const EntraIdPublicIdentityAuthority = "login.microsoftonline.com";
|
const EntraIdPublicIdentityAuthority = "login.microsoftonline.com";
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { config as dotenvConfig } from "dotenv";
|
||||||
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { I18nService } from "../../../jslib/common/src/abstractions/i18n.service";
|
||||||
|
import { LogService } from "../../../jslib/common/src/abstractions/log.service";
|
||||||
|
import {
|
||||||
|
getGSuiteConfiguration,
|
||||||
|
getSyncConfiguration,
|
||||||
|
} from "../../../utils/google-workspace/config-fixtures";
|
||||||
|
import { groupFixtures } from "../../../utils/google-workspace/group-fixtures";
|
||||||
|
import { userFixtures } from "../../../utils/google-workspace/user-fixtures";
|
||||||
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
|
import { StateService } from "../state.service";
|
||||||
|
|
||||||
|
import { GSuiteDirectoryService } from "./gsuite-directory.service";
|
||||||
|
|
||||||
|
// These tests integrate with a test Google Workspace instance.
|
||||||
|
// Credentials are located in the shared Bitwarden collection for Directory Connector testing.
|
||||||
|
// Place the .env file attachment in the utils folder.
|
||||||
|
|
||||||
|
// Load .env variables
|
||||||
|
dotenvConfig({ path: "utils/.env" });
|
||||||
|
|
||||||
|
// These filters target integration test data.
|
||||||
|
// These should return data that matches the user and group fixtures exactly.
|
||||||
|
// There may be additional data present if not used.
|
||||||
|
const INTEGRATION_USER_FILTER = "|orgUnitPath='/Integration testing'";
|
||||||
|
const INTEGRATION_GROUP_FILTER = "|name:Integration*";
|
||||||
|
|
||||||
|
// These tests are slow!
|
||||||
|
// Increase the default timeout from 5s to 15s
|
||||||
|
jest.setTimeout(15000);
|
||||||
|
|
||||||
|
describe("gsuiteDirectoryService", () => {
|
||||||
|
let logService: MockProxy<LogService>;
|
||||||
|
let i18nService: MockProxy<I18nService>;
|
||||||
|
let stateService: MockProxy<StateService>;
|
||||||
|
|
||||||
|
let directoryService: GSuiteDirectoryService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
logService = mock();
|
||||||
|
i18nService = mock();
|
||||||
|
stateService = mock();
|
||||||
|
|
||||||
|
stateService.getDirectoryType.mockResolvedValue(DirectoryType.GSuite);
|
||||||
|
stateService.getLastUserSync.mockResolvedValue(null); // do not filter results by last modified date
|
||||||
|
i18nService.t.mockImplementation((id) => id); // passthrough implementation for any error messages
|
||||||
|
|
||||||
|
directoryService = new GSuiteDirectoryService(logService, i18nService, stateService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("syncs without using filters (includes test data)", async () => {
|
||||||
|
const directoryConfig = getGSuiteConfiguration();
|
||||||
|
stateService.getDirectory.calledWith(DirectoryType.GSuite).mockResolvedValue(directoryConfig);
|
||||||
|
|
||||||
|
const syncConfig = getSyncConfiguration({
|
||||||
|
groups: true,
|
||||||
|
users: true,
|
||||||
|
});
|
||||||
|
stateService.getSync.mockResolvedValue(syncConfig);
|
||||||
|
|
||||||
|
const result = await directoryService.getEntries(true, true);
|
||||||
|
|
||||||
|
expect(result[0]).toEqual(expect.arrayContaining(groupFixtures));
|
||||||
|
expect(result[1]).toEqual(expect.arrayContaining(userFixtures));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("syncs using user and group filters (exact match for test data)", async () => {
|
||||||
|
const directoryConfig = getGSuiteConfiguration();
|
||||||
|
stateService.getDirectory.calledWith(DirectoryType.GSuite).mockResolvedValue(directoryConfig);
|
||||||
|
|
||||||
|
const syncConfig = getSyncConfiguration({
|
||||||
|
groups: true,
|
||||||
|
users: true,
|
||||||
|
userFilter: INTEGRATION_USER_FILTER,
|
||||||
|
groupFilter: INTEGRATION_GROUP_FILTER,
|
||||||
|
});
|
||||||
|
stateService.getSync.mockResolvedValue(syncConfig);
|
||||||
|
|
||||||
|
const result = await directoryService.getEntries(true, true);
|
||||||
|
|
||||||
|
expect(result).toEqual([groupFixtures, userFixtures]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,14 +4,14 @@ import { admin_directory_v1, google } from "googleapis";
|
|||||||
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
||||||
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
||||||
|
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../../abstractions/state.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../../models/groupEntry";
|
||||||
import { GSuiteConfiguration } from "../models/gsuiteConfiguration";
|
import { GSuiteConfiguration } from "../../models/gsuiteConfiguration";
|
||||||
import { SyncConfiguration } from "../models/syncConfiguration";
|
import { SyncConfiguration } from "../../models/syncConfiguration";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../../models/userEntry";
|
||||||
|
import { BaseDirectoryService } from "../baseDirectory.service";
|
||||||
|
|
||||||
import { BaseDirectoryService } from "./baseDirectory.service";
|
|
||||||
import { IDirectoryService } from "./directory.service";
|
import { IDirectoryService } from "./directory.service";
|
||||||
|
|
||||||
export class GSuiteDirectoryService extends BaseDirectoryService implements IDirectoryService {
|
export class GSuiteDirectoryService extends BaseDirectoryService implements IDirectoryService {
|
||||||
@@ -253,7 +253,15 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements IDir
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.client.authorize();
|
try {
|
||||||
|
await this.client.authorize();
|
||||||
|
} catch (error) {
|
||||||
|
// Catch and rethrow this to sanitize any sensitive info (e.g. private key) in the error message
|
||||||
|
this.logService.error(
|
||||||
|
`Google Workspace authentication failed: ${error?.name || "Unknown error"}`,
|
||||||
|
);
|
||||||
|
throw new Error(this.i18nService.t("authenticationFailed"));
|
||||||
|
}
|
||||||
|
|
||||||
this.authParams = {
|
this.authParams = {
|
||||||
auth: this.client,
|
auth: this.client,
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
|
import { I18nService } from "../../../jslib/common/src/abstractions/i18n.service";
|
||||||
import { LogService } from "../../jslib/common/src/abstractions/log.service";
|
import { LogService } from "../../../jslib/common/src/abstractions/log.service";
|
||||||
import { groupFixtures } from "../../openldap/group-fixtures";
|
import {
|
||||||
import { userFixtures } from "../../openldap/user-fixtures";
|
getLdapConfiguration,
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
getSyncConfiguration,
|
||||||
import { getLdapConfiguration, getSyncConfiguration } from "../utils/test-fixtures";
|
} from "../../../utils/openldap/config-fixtures";
|
||||||
|
import { groupFixtures } from "../../../utils/openldap/group-fixtures";
|
||||||
|
import { userFixtures } from "../../../utils/openldap/user-fixtures";
|
||||||
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
|
import { StateService } from "../state.service";
|
||||||
|
|
||||||
import { LdapDirectoryService } from "./ldap-directory.service";
|
import { LdapDirectoryService } from "./ldap-directory.service";
|
||||||
import { StateService } from "./state.service";
|
|
||||||
|
|
||||||
// These tests integrate with the OpenLDAP docker image and seed data located in the openldap folder.
|
// These tests integrate with the OpenLDAP docker image and seed data located in the openldap folder.
|
||||||
// To run theses tests:
|
// To run theses tests:
|
||||||
@@ -52,7 +55,7 @@ describe("ldapDirectoryService", () => {
|
|||||||
getLdapConfiguration({
|
getLdapConfiguration({
|
||||||
ssl: true,
|
ssl: true,
|
||||||
startTls: true,
|
startTls: true,
|
||||||
tlsCaPath: "./openldap/certs/rootCA.pem",
|
tlsCaPath: "./utils/openldap/certs/rootCA.pem",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
|
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
|
||||||
@@ -67,7 +70,7 @@ describe("ldapDirectoryService", () => {
|
|||||||
getLdapConfiguration({
|
getLdapConfiguration({
|
||||||
port: 1636,
|
port: 1636,
|
||||||
ssl: true,
|
ssl: true,
|
||||||
sslCaPath: "./openldap/certs/rootCA.pem",
|
sslCaPath: "./utils/openldap/certs/rootCA.pem",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
|
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
|
||||||
@@ -7,12 +7,12 @@ import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
|||||||
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
||||||
import { Utils } from "@/jslib/common/src/misc/utils";
|
import { Utils } from "@/jslib/common/src/misc/utils";
|
||||||
|
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../../abstractions/state.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../../models/groupEntry";
|
||||||
import { LdapConfiguration } from "../models/ldapConfiguration";
|
import { LdapConfiguration } from "../../models/ldapConfiguration";
|
||||||
import { SyncConfiguration } from "../models/syncConfiguration";
|
import { SyncConfiguration } from "../../models/syncConfiguration";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../../models/userEntry";
|
||||||
|
|
||||||
import { IDirectoryService } from "./directory.service";
|
import { IDirectoryService } from "./directory.service";
|
||||||
|
|
||||||
@@ -3,14 +3,14 @@ import * as https from "https";
|
|||||||
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
||||||
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
||||||
|
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../../abstractions/state.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../../models/groupEntry";
|
||||||
import { OktaConfiguration } from "../models/oktaConfiguration";
|
import { OktaConfiguration } from "../../models/oktaConfiguration";
|
||||||
import { SyncConfiguration } from "../models/syncConfiguration";
|
import { SyncConfiguration } from "../../models/syncConfiguration";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../../models/userEntry";
|
||||||
|
import { BaseDirectoryService } from "../baseDirectory.service";
|
||||||
|
|
||||||
import { BaseDirectoryService } from "./baseDirectory.service";
|
|
||||||
import { IDirectoryService } from "./directory.service";
|
import { IDirectoryService } from "./directory.service";
|
||||||
|
|
||||||
const DelayBetweenBuildGroupCallsInMilliseconds = 500;
|
const DelayBetweenBuildGroupCallsInMilliseconds = 500;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
||||||
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
||||||
|
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../../abstractions/state.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../../enums/directoryType";
|
||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../../models/groupEntry";
|
||||||
import { OneLoginConfiguration } from "../models/oneLoginConfiguration";
|
import { OneLoginConfiguration } from "../../models/oneLoginConfiguration";
|
||||||
import { SyncConfiguration } from "../models/syncConfiguration";
|
import { SyncConfiguration } from "../../models/syncConfiguration";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../../models/userEntry";
|
||||||
|
import { BaseDirectoryService } from "../baseDirectory.service";
|
||||||
|
|
||||||
import { BaseDirectoryService } from "./baseDirectory.service";
|
|
||||||
import { IDirectoryService } from "./directory.service";
|
import { IDirectoryService } from "./directory.service";
|
||||||
|
|
||||||
// Basic email validation: something@something.something
|
// Basic email validation: something@something.something
|
||||||
@@ -2,8 +2,8 @@ import { GetUniqueString } from "@/jslib/common/spec/utils";
|
|||||||
|
|
||||||
import { UserEntry } from "@/src/models/userEntry";
|
import { UserEntry } from "@/src/models/userEntry";
|
||||||
|
|
||||||
|
import { groupSimulator, userSimulator } from "../../utils/request-builder-helper";
|
||||||
import { RequestBuilderOptions } from "../abstractions/request-builder.service";
|
import { RequestBuilderOptions } from "../abstractions/request-builder.service";
|
||||||
import { groupSimulator, userSimulator } from "../utils/request-builder-helper";
|
|
||||||
|
|
||||||
import { SingleRequestBuilder } from "./single-request-builder";
|
import { SingleRequestBuilder } from "./single-request-builder";
|
||||||
|
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ import { EnvironmentService } from "@/jslib/common/src/services/environment.serv
|
|||||||
|
|
||||||
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
|
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
|
||||||
import { LogService } from "../../jslib/common/src/abstractions/log.service";
|
import { LogService } from "../../jslib/common/src/abstractions/log.service";
|
||||||
import { groupFixtures } from "../../openldap/group-fixtures";
|
import { getLdapConfiguration, getSyncConfiguration } from "../../utils/openldap/config-fixtures";
|
||||||
import { userFixtures } from "../../openldap/user-fixtures";
|
|
||||||
import { DirectoryFactoryService } from "../abstractions/directory-factory.service";
|
import { DirectoryFactoryService } from "../abstractions/directory-factory.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../enums/directoryType";
|
||||||
import { getLdapConfiguration, getSyncConfiguration } from "../utils/test-fixtures";
|
|
||||||
|
|
||||||
import { BatchRequestBuilder } from "./batch-request-builder";
|
import { BatchRequestBuilder } from "./batch-request-builder";
|
||||||
import { LdapDirectoryService } from "./ldap-directory.service";
|
import { LdapDirectoryService } from "./directory-services/ldap-directory.service";
|
||||||
import { SingleRequestBuilder } from "./single-request-builder";
|
import { SingleRequestBuilder } from "./single-request-builder";
|
||||||
import { StateService } from "./state.service";
|
import { StateService } from "./state.service";
|
||||||
import { SyncService } from "./sync.service";
|
import { SyncService } from "./sync.service";
|
||||||
import * as constants from "./sync.service";
|
import * as constants from "./sync.service";
|
||||||
|
|
||||||
|
import { groupFixtures } from "@/utils/openldap/group-fixtures";
|
||||||
|
import { userFixtures } from "@/utils/openldap/user-fixtures";
|
||||||
|
|
||||||
describe("SyncService", () => {
|
describe("SyncService", () => {
|
||||||
let logService: MockProxy<LogService>;
|
let logService: MockProxy<LogService>;
|
||||||
let i18nService: MockProxy<I18nService>;
|
let i18nService: MockProxy<I18nService>;
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ import { MessagingService } from "@/jslib/common/src/abstractions/messaging.serv
|
|||||||
import { OrganizationImportRequest } from "@/jslib/common/src/models/request/organizationImportRequest";
|
import { OrganizationImportRequest } from "@/jslib/common/src/models/request/organizationImportRequest";
|
||||||
import { ApiService } from "@/jslib/common/src/services/api.service";
|
import { ApiService } from "@/jslib/common/src/services/api.service";
|
||||||
|
|
||||||
|
import { getSyncConfiguration } from "../../utils/openldap/config-fixtures";
|
||||||
import { DirectoryFactoryService } from "../abstractions/directory-factory.service";
|
import { DirectoryFactoryService } from "../abstractions/directory-factory.service";
|
||||||
import { DirectoryType } from "../enums/directoryType";
|
import { DirectoryType } from "../enums/directoryType";
|
||||||
import { getSyncConfiguration } from "../utils/test-fixtures";
|
|
||||||
|
|
||||||
import { BatchRequestBuilder } from "./batch-request-builder";
|
import { BatchRequestBuilder } from "./batch-request-builder";
|
||||||
|
import { LdapDirectoryService } from "./directory-services/ldap-directory.service";
|
||||||
import { I18nService } from "./i18n.service";
|
import { I18nService } from "./i18n.service";
|
||||||
import { LdapDirectoryService } from "./ldap-directory.service";
|
|
||||||
import { SingleRequestBuilder } from "./single-request-builder";
|
import { SingleRequestBuilder } from "./single-request-builder";
|
||||||
import { StateService } from "./state.service";
|
import { StateService } from "./state.service";
|
||||||
import { SyncService } from "./sync.service";
|
import { SyncService } from "./sync.service";
|
||||||
import * as constants from "./sync.service";
|
import * as constants from "./sync.service";
|
||||||
|
|
||||||
import { groupFixtures } from "@/openldap/group-fixtures";
|
import { groupFixtures } from "@/utils/openldap/group-fixtures";
|
||||||
import { userFixtures } from "@/openldap/user-fixtures";
|
import { userFixtures } from "@/utils/openldap/user-fixtures";
|
||||||
|
|
||||||
describe("SyncService", () => {
|
describe("SyncService", () => {
|
||||||
let cryptoFunctionService: MockProxy<CryptoFunctionService>;
|
let cryptoFunctionService: MockProxy<CryptoFunctionService>;
|
||||||
|
|||||||
4
utils/.env.example
Normal file
4
utils/.env.example
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
GOOGLE_DOMAIN=
|
||||||
|
GOOGLE_ADMIN_USER=
|
||||||
|
GOOGLE_CLIENT_EMAIL=
|
||||||
|
GOOGLE_PRIVATE_KEY=
|
||||||
56
utils/google-workspace/config-fixtures.ts
Normal file
56
utils/google-workspace/config-fixtures.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { GSuiteConfiguration } from "../../src/models/gsuiteConfiguration";
|
||||||
|
import { SyncConfiguration } from "../../src/models/syncConfiguration";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns a basic GSuite configuration. Can be overridden by passing in a partial configuration.
|
||||||
|
*/
|
||||||
|
export const getGSuiteConfiguration = (
|
||||||
|
config?: Partial<GSuiteConfiguration>,
|
||||||
|
): GSuiteConfiguration => {
|
||||||
|
const adminUser = process.env.GOOGLE_ADMIN_USER;
|
||||||
|
const clientEmail = process.env.GOOGLE_CLIENT_EMAIL;
|
||||||
|
const privateKey = process.env.GOOGLE_PRIVATE_KEY;
|
||||||
|
const domain = process.env.GOOGLE_DOMAIN;
|
||||||
|
|
||||||
|
if (!adminUser || !clientEmail || !privateKey || !domain) {
|
||||||
|
throw new Error("Google Workspace integration test credentials not configured.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// TODO
|
||||||
|
adminUser,
|
||||||
|
clientEmail,
|
||||||
|
privateKey,
|
||||||
|
domain: domain,
|
||||||
|
customer: "",
|
||||||
|
...(config ?? {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns a basic Google Workspace sync configuration. Can be overridden by passing in a partial configuration.
|
||||||
|
*/
|
||||||
|
export const getSyncConfiguration = (config?: Partial<SyncConfiguration>): SyncConfiguration => ({
|
||||||
|
users: false,
|
||||||
|
groups: false,
|
||||||
|
interval: 5,
|
||||||
|
userFilter: "",
|
||||||
|
groupFilter: "",
|
||||||
|
removeDisabled: false,
|
||||||
|
overwriteExisting: false,
|
||||||
|
largeImport: false,
|
||||||
|
// Ldap properties - not optional for some reason
|
||||||
|
groupObjectClass: "",
|
||||||
|
userObjectClass: "",
|
||||||
|
groupPath: null,
|
||||||
|
userPath: null,
|
||||||
|
groupNameAttribute: "",
|
||||||
|
userEmailAttribute: "",
|
||||||
|
memberAttribute: "",
|
||||||
|
useEmailPrefixSuffix: false,
|
||||||
|
emailPrefixAttribute: "",
|
||||||
|
emailSuffix: null,
|
||||||
|
creationDateAttribute: "",
|
||||||
|
revisionDateAttribute: "",
|
||||||
|
...(config ?? {}),
|
||||||
|
});
|
||||||
26
utils/google-workspace/group-fixtures.ts
Normal file
26
utils/google-workspace/group-fixtures.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
|
import { GroupEntry } from "../../src/models/groupEntry";
|
||||||
|
|
||||||
|
// These must match the Google Workspace seed data
|
||||||
|
|
||||||
|
const data: Jsonify<GroupEntry>[] = [
|
||||||
|
{
|
||||||
|
externalId: "0319y80a3anpxhj",
|
||||||
|
groupMemberReferenceIds: [],
|
||||||
|
name: "Integration Test Group A",
|
||||||
|
referenceId: "0319y80a3anpxhj",
|
||||||
|
userMemberExternalIds: ["111605910541641314041", "111147009830456099026"],
|
||||||
|
users: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
externalId: "02afmg28317uyub",
|
||||||
|
groupMemberReferenceIds: [],
|
||||||
|
name: "Integration Test Group B",
|
||||||
|
referenceId: "02afmg28317uyub",
|
||||||
|
userMemberExternalIds: ["111147009830456099026", "100150970267699397306"],
|
||||||
|
users: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const groupFixtures = data.map((g) => GroupEntry.fromJSON(g));
|
||||||
50
utils/google-workspace/user-fixtures.ts
Normal file
50
utils/google-workspace/user-fixtures.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
|
import { UserEntry } from "../../src/models/userEntry";
|
||||||
|
|
||||||
|
// These must match the Google Workspace seed data
|
||||||
|
|
||||||
|
const data: Jsonify<UserEntry>[] = [
|
||||||
|
// In Group A
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
disabled: false,
|
||||||
|
email: "testuser1@bwrox.dev",
|
||||||
|
externalId: "111605910541641314041",
|
||||||
|
referenceId: "111605910541641314041",
|
||||||
|
},
|
||||||
|
// In Groups A + B
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
disabled: false,
|
||||||
|
email: "testuser2@bwrox.dev",
|
||||||
|
externalId: "111147009830456099026",
|
||||||
|
referenceId: "111147009830456099026",
|
||||||
|
},
|
||||||
|
// In Group B
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
disabled: false,
|
||||||
|
email: "testuser3@bwrox.dev",
|
||||||
|
externalId: "100150970267699397306",
|
||||||
|
referenceId: "100150970267699397306",
|
||||||
|
},
|
||||||
|
// Not in a group
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
disabled: false,
|
||||||
|
email: "testuser4@bwrox.dev",
|
||||||
|
externalId: "113764752650306721470",
|
||||||
|
referenceId: "113764752650306721470",
|
||||||
|
},
|
||||||
|
// Disabled user
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
disabled: true,
|
||||||
|
email: "testuser5@bwrox.dev",
|
||||||
|
externalId: "110381976819725658200",
|
||||||
|
referenceId: "110381976819725658200",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const userFixtures = data.map((g) => UserEntry.fromJSON(g));
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LdapConfiguration } from "../models/ldapConfiguration";
|
import { LdapConfiguration } from "../../src/models/ldapConfiguration";
|
||||||
import { SyncConfiguration } from "../models/syncConfiguration";
|
import { SyncConfiguration } from "../../src/models/syncConfiguration";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns a basic ldap configuration without TLS/SSL enabled. Can be overridden by passing in a partial configuration.
|
* @returns a basic ldap configuration without TLS/SSL enabled. Can be overridden by passing in a partial configuration.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { GroupEntry } from "../src/models/groupEntry";
|
import { GroupEntry } from "@/src/models/groupEntry";
|
||||||
|
|
||||||
// These must match the ldap server seed data in directory.ldif
|
// These must match the ldap server seed data in directory.ldif
|
||||||
const data: Jsonify<GroupEntry>[] = [
|
const data: Jsonify<GroupEntry>[] = [
|
||||||
10
utils/openldap/mkcert.sh
Executable file
10
utils/openldap/mkcert.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
if ! [ -x "$(command -v mkcert)" ]; then
|
||||||
|
echo 'Error: mkcert is not installed. Install mkcert first and then re-run this script.'
|
||||||
|
echo 'e.g. brew install mkcert'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkcert -install
|
||||||
|
mkdir -p ./utils/openldap/certs
|
||||||
|
cp "$(mkcert -CAROOT)/rootCA.pem" ./utils/openldap/certs/rootCA.pem
|
||||||
|
mkcert -key-file ./utils/openldap/certs/openldap-key.pem -cert-file ./utils/openldap/certs/openldap.pem localhost openldap
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { UserEntry } from "../src/models/userEntry";
|
import { UserEntry } from "@/src/models/userEntry";
|
||||||
|
|
||||||
// These must match the ldap server seed data in directory.ldif
|
// These must match the ldap server seed data in directory.ldif
|
||||||
const data: Jsonify<UserEntry>[] = [
|
const data: Jsonify<UserEntry>[] = [
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { GetUniqueString } from "@/jslib/common/spec/utils";
|
import { GetUniqueString } from "@/jslib/common/spec/utils";
|
||||||
|
|
||||||
import { GroupEntry } from "../models/groupEntry";
|
import { GroupEntry } from "../src/models/groupEntry";
|
||||||
import { UserEntry } from "../models/userEntry";
|
import { UserEntry } from "../src/models/userEntry";
|
||||||
|
|
||||||
export function userSimulator(userCount: number): UserEntry[] {
|
export function userSimulator(userCount: number): UserEntry[] {
|
||||||
const users: UserEntry[] = [];
|
const users: UserEntry[] = [];
|
||||||
Reference in New Issue
Block a user