diff --git a/.github/workflows/azure-devops-pipelines-cg.yml b/.github/workflows/azure-devops-pipelines-cg.yml index 9cd8e190..086abdc3 100644 --- a/.github/workflows/azure-devops-pipelines-cg.yml +++ b/.github/workflows/azure-devops-pipelines-cg.yml @@ -1,7 +1,7 @@ # This pipeline will be triggered when either main branch is pushed or 2AM on workdays. variables: - name: tags - value: "nonproduction" + value: "production" readonly: true trigger: branches: @@ -20,7 +20,7 @@ resources: name: 1ESPipelineTemplates/OfficePipelineTemplates ref: refs/tags/release extends: - template: v1/Office.Unofficial.PipelineTemplate.yml@CustomPipelineTemplates + template: v1/Office.Official.PipelineTemplate.yml@CustomPipelineTemplates parameters: pool: name: Azure-Pipelines-1ESPT-ExDShared diff --git a/.github/workflows/release-azure-pipelines.yml b/.github/workflows/release-azure-pipelines.yml index 0767004c..2b393026 100644 --- a/.github/workflows/release-azure-pipelines.yml +++ b/.github/workflows/release-azure-pipelines.yml @@ -2,7 +2,6 @@ parameters: - name: version type: string - default: 0.8.6 - name: prerelease displayName: Prerelease? type: boolean @@ -31,24 +30,28 @@ parameters: variables: - name: tags - value: "nonproduction" + value: "production" readonly: true - name: pythonVersion value: 3.10 readonly: true +- name: artifactsPath + value: $(Build.ArtifactStagingDirectory)/azureauth-${{ parameters.version }} + readonly: true trigger: none + pr: none resources: repositories: - - repository: CustomPipelineTemplates + - repository: OfficePipelineTemplates type: git name: 1ESPipelineTemplates/OfficePipelineTemplates ref: refs/tags/release extends: - template: v1/Office.Unofficial.PipelineTemplate.yml@CustomPipelineTemplates + template: v1/Office.Official.PipelineTemplate.yml@OfficePipelineTemplates parameters: pool: name: Azure-Pipelines-1ESPT-ExDShared @@ -59,6 +62,9 @@ extends: name: Azure-Pipelines-1ESPT-ExDShared image: windows-latest os: windows + # This prevents auto-injected Roslyn task from running the build again. + roslyn: + copyLogsOnly: true stages: - stage: validate displayName: Validate @@ -66,16 +72,16 @@ extends: - job: validate displayName: Validate steps: - - checkout: self - - task: UsePythonVersion@0 - displayName: Use Python $(pythonVersion) - inputs: - versionSpec: $(pythonVersion) - - task: Bash@3 - inputs: - targetType: inline - script: | - echo ${{ parameters.version }} | python ./bin/version.py + - checkout: self + - task: UsePythonVersion@0 + displayName: Use Python $(pythonVersion) + inputs: + versionSpec: $(pythonVersion) + - task: Bash@3 + inputs: + targetType: inline + script: | + echo ${{ parameters.version }} | python ./bin/version.py - stage: build displayName: Build @@ -93,40 +99,278 @@ extends: targetPath: dist/${{ config.runtime }} artifactName: azureauth-${{ parameters.version }}-${{ config.runtime }} steps: - - checkout: self - - task: UseDotNet@2 - displayName: Use .NET Core sdk 6.x - inputs: - version: 6.x + - checkout: self + - task: UseDotNet@2 + displayName: Use .NET Core sdk 6.x + inputs: + version: 6.x + - task: NuGetToolInstaller@0 + displayName: Use NuGet 6.x + inputs: + versionSpec: 6.x + - task: DotNetCoreCLI@2 + displayName: Install dependencies + inputs: + command: restore + feedsToUse: select + vstsFeed: $(VSTS_FEED_ID) + includeNuGetOrg: false + arguments: --runtime ${{ config.runtime }} + # 1ES PT requires explicit build task for Roslyn analysis. Auto-injected Roslyn task will use build logs from this build. + - task: DotNetCoreCLI@2 + displayName: Build projects + inputs: + command: 'build' + projects: '**/*.csproj' + - task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + arguments: --configuration release --no-restore + - task: DotNetCoreCLI@2 + displayName: Build artifacts + env: + ADO_TOKEN: $(System.AccessToken) + inputs: + command: publish + projects: src/AzureAuth/AzureAuth.csproj + arguments: -p:Version=${{ parameters.version }} --configuration release --self-contained true --runtime ${{ config.runtime }} --output dist/${{ config.runtime }} + publishWebProjects: false + zipAfterPublish: false + modifyOutputPath: true - - task: NuGetToolInstaller@0 - displayName: Use NuGet 6.x - inputs: - versionSpec: 6.x + - stage: sign + displayName: Sign + dependsOn: build + jobs: + - ${{ each config in parameters.buildConfigs }}: + - job: sign_${{ replace(config.runtime,'-', '_') }} + displayName: Signing ${{ config.runtime }} + pool: + name: Azure-Pipelines-1ESPT-ExDShared + image: windows-latest + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: azureauth-${{ parameters.version }}-${{ config.runtime }} + targetPath: $(artifactsPath)-${{ config.runtime }} + outputs: + - output: pipelineArtifact + artifactName: azureauth-${{ parameters.version }}-${{ config.runtime }}-signed + targetPath: $(artifactsPath)-${{ config.runtime }}-signed + steps: + - task: EsrpCodeSigning@5 + displayName: Sign artifacts win10-x64 + condition: eq('${{ config.runtime }}', 'win10-x64') + inputs: + ConnectedServiceName: $(ESRP_KV_SERVICE_CONNECTION) + AppRegistrationClientId: $(SIGNING_AAD_ID) + AppRegistrationTenantId: $(SIGNING_TENANT_ID) + AuthAKVName: $(AZURE_VAULT) + AuthCertName: $(AZURE_VAULT_ESRP_AAD_CERT_NAME) + AuthSignCertName: $(AZURE_VAULT_ESRP_REQ_CERT_NAME) + FolderPath: $(artifactsPath)-${{ config.runtime }}/AzureAuth + Pattern: '*.dll,*.exe' + signConfigType: 'inlineSignParams' + inlineOperation: | + [ + { + "KeyCode": "$(SIGNING_KEY_CODE_AUTHENTICODE)", + "OperationCode": "SigntoolSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": { + "OpusName": "Microsoft", + "OpusInfo": "https://www.microsoft.com", + "FileDigest": "/fd SHA256", + "PageHash": "/NPH", + "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + }, + { + "KeyCode": "$(SIGNING_KEY_CODE_AUTHENTICODE)", + "OperationCode": "SigntoolVerify", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": {} + } + ] + SessionTimeout: '60' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + PendingAnalysisWaitTimeoutMinutes: '5' + # We need to zip the artifacts for osx before sending to ESRP for signing. + - task: ArchiveFiles@2 + displayName: Codesigning - zip artifacts to send to ESRP + condition: startsWith('${{ config.runtime }}', 'osx') + inputs: + rootFolderOrFile: $(artifactsPath)-${{ config.runtime }} + includeRootFolder: false + archiveType: zip + archiveFile: $(artifactsPath)-${{ config.runtime }}.zip + - task: EsrpCodeSigning@5 + displayName: Sign artifacts osx + condition: startsWith('${{ config.runtime }}', 'osx') + inputs: + ConnectedServiceName: $(ESRP_KV_SERVICE_CONNECTION) + AppRegistrationClientId: $(SIGNING_AAD_ID) + AppRegistrationTenantId: $(SIGNING_TENANT_ID) + AuthAKVName: $(AZURE_VAULT) + AuthCertName: $(AZURE_VAULT_ESRP_AAD_CERT_NAME) + AuthSignCertName: $(AZURE_VAULT_ESRP_REQ_CERT_NAME) + FolderPath: $(Build.ArtifactStagingDirectory) + Pattern: 'azureauth-${{ parameters.version }}-${{ config.runtime }}.zip' + signConfigType: 'inlineSignParams' + inlineOperation: | + [ + { + "KeyCode": "$(SIGNING_KEY_CODE_MAC)", + "OperationCode": "MacAppDeveloperSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": {} + }, + { + "KeyCode": "$(SIGNING_KEY_CODE_MAC)", + "OperationCode": "SigntoolVerify", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": {} + } + ] + SessionTimeout: '60' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + PendingAnalysisWaitTimeoutMinutes: '5' + - task: ExtractFiles@1 + displayName: Extract signed artifacts osx + condition: startsWith('${{ config.runtime }}', 'osx') + inputs: + archiveFilePatterns: $(artifactsPath)-${{ config.runtime }}.zip + destinationFolder: $(artifactsPath)-${{ config.runtime }} + cleanDestinationFolder: true + overwriteExistingFiles: true + # We rename the signed artifacts to avoid conflicts with the unsigned pipeline artifacts from the previous stage. + - task: PowerShell@2 + displayName: Rename signed artifacts + inputs: + workingDirectory: $(Build.ArtifactStagingDirectory) + targetType: 'inline' + script: | + mv "azureauth-${{ parameters.version }}-${{ config.runtime }}" "azureauth-${{ parameters.version }}-${{ config.runtime }}-signed" - - task: DotNetCoreCLI@2 - displayName: Install dependencies - inputs: - command: restore - feedsToUse: select - vstsFeed: $(vstsFeedId) - includeNuGetOrg: false - arguments: --runtime ${{ config.runtime }} + # Currently we package artifacts into the most commonly accessible archive format for their respective platforms. + - stage: package + displayName: Package + dependsOn: sign + jobs: + - job: package + displayName: Package + pool: + name: Azure-Pipelines-1ESPT-ExDShared + image: ubuntu-latest + os: linux + templateContext: + inputs: + - ${{ each config in parameters.buildConfigs }}: + - input: pipelineArtifact + artifactName: azureauth-${{ parameters.version }}-${{ config.runtime }}-signed + targetPath: $(artifactsPath)-${{ config.runtime }}-signed + outputs: + - output: pipelineArtifact + artifactName: azureauth-${{ parameters.version }}-packaged + targetPath: $(artifactsPath)-packaged + steps: + - task: PowerShell@2 + displayName: Create directory to place packaged artifacts + inputs: + workingDirectory: $(Build.ArtifactStagingDirectory) + targetType: 'inline' + script: | + mkdir azureauth-${{ parameters.version }}-packaged + - task: ArchiveFiles@2 + displayName: Create win10-x64 archive + inputs: + rootFolderOrFile: $(artifactsPath)-win10-x64-signed/AzureAuth + includeRootFolder: false + archiveType: zip + archiveFile: $(artifactsPath)-packaged/azureauth-${{ parameters.version }}-win10-x64.zip + - task: Bash@3 + displayName: Prepare osx-x64 executables + inputs: + targetType: inline + workingDirectory: $(Build.ArtifactStagingDirectory) + script: | + cd azureauth-${{ parameters.version }}-osx-x64-signed/AzureAuth + chmod +x azureauth createdump *.dylib + - task: ArchiveFiles@2 + displayName: Create osx-x64 archive + inputs: + rootFolderOrFile: $(artifactsPath)-osx-x64-signed/AzureAuth + includeRootFolder: false + archiveType: tar + tarCompression: gz + archiveFile: $(artifactsPath)-packaged/azureauth-${{ parameters.version }}-osx-x64.tar.gz + - task: Bash@3 + displayName: Prepare osx-arm64 executables + inputs: + workingDirectory: $(Build.ArtifactStagingDirectory) + targetType: inline + script: | + cd azureauth-${{ parameters.version }}-osx-arm64-signed/AzureAuth + chmod +x azureauth createdump *.dylib + - task: ArchiveFiles@2 + displayName: Create osx-arm64 archive + inputs: + rootFolderOrFile: $(artifactsPath)-osx-arm64-signed/AzureAuth + includeRootFolder: false + archiveType: tar + tarCompression: gz + archiveFile: $(artifactsPath)-packaged/azureauth-${{ parameters.version }}-osx-arm64.tar.gz - - task: DotNetCoreCLI@2 - displayName: Test - inputs: - command: test - arguments: --configuration release --no-restore - - - task: DotNetCoreCLI@2 - displayName: Build artifacts - env: - ADO_TOKEN: $(System.AccessToken) - inputs: - command: publish - projects: src/AzureAuth/AzureAuth.csproj - arguments: -p:Version=${{ parameters.version }} --configuration release --self-contained true --runtime ${{ config.runtime }} --output dist/${{ config.runtime }} - publishWebProjects: false - zipAfterPublish: false - modifyOutputPath: true \ No newline at end of file + - stage: release + displayName: Release + dependsOn: package + jobs: + - job: approval + displayName: Manual Approval + pool: server + timeoutInMinutes: 5760 # job times out in 4 days + steps: + - task: ManualValidation@0 + timeoutInMinutes: 4320 # task times out in 3 days + inputs: + notifyUsers: $(REVIEWER) + instructions: 'Review the AzureAuth GitHub Release.' + - job: release + displayName: Release + dependsOn: approval + pool: + name: Azure-Pipelines-1ESPT-ExDShared + image: ubuntu-latest + os: linux + templateContext: + inputs: + - input: pipelineArtifact + artifactName: azureauth-${{ parameters.version }}-packaged + targetPath: $(artifactsPath)-packaged + steps: + - task: GitHubRelease@1 + displayName: Create AzureAuth GitHub Release + inputs: + gitHubConnection: $(GITHUB_RELEASE_SERVICE_CONNECTION) + repositoryName: 'AzureAD/microsoft-authentication-cli' + action: 'create' + target: $(Build.SourceVersion) + tagSource: 'userSpecifiedTag' + tag: ${{ parameters.version }} + isPrerelease: ${{ parameters.prerelease }} + isDraft: false + addChangeLog: false + releaseNotesSource: 'inline' + releaseNotesInline: "Release ${{ parameters.version }}. See [`CHANGELOG.md`](https://github.com/AzureAD/microsoft-authentication-cli/blob/${{ parameters.version }}/CHANGELOG.md) for updates." + assets: | + $(artifactsPath)-packaged/azureauth-${{ parameters.version }}-win10-x64.zip + $(artifactsPath)-packaged/azureauth-${{ parameters.version }}-osx-x64.tar.gz + $(artifactsPath)-packaged/azureauth-${{ parameters.version }}-osx-arm64.tar.gz \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index fd96d892..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,269 +0,0 @@ -name: Release - -on: - workflow_dispatch: - inputs: - version: - description: 'Version' - required: true - type: string - # We use SemVer, anything before 1.0.0 is a pre-release, but this could also include versions like 1.1.0-beta. - prerelease: - description: 'Prerelease' - required: true - default: true - type: boolean - -jobs: - # Special request from @kyle-rader and @goagain, so no one can create an invalid release. - validate: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Validate version - run: echo ${{ github.event.inputs.version }} | python ./bin/version.py - - build: - permissions: - actions: read - contents: read - security-events: write - statuses: write - id-token: write - - runs-on: ${{ matrix.os }} - needs: [validate] - strategy: - matrix: - # We build on Linux, but don't yet ship Linux because we can't easily sign those releases. - runtime: [osx-x64, osx-arm64, win10-x64] - include: - # macos-latest (currently 14) breaks this flow. Refer https://github.com/actions/runner-images/issues/9766. - - runtime: osx-x64 - os: macos-13 - - runtime: osx-arm64 - os: macos-13 - - runtime: win10-x64 - os: windows-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Get Azure DevOps Access Token - id: getToken - uses: "./.github/actions/get-ado-token" - with: - client-id: ${{ secrets.AZURE_RELEASE_WORKFLOW_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_RELEASE_WORKFLOW_TENANT_ID }} - organization: ${{ secrets.ADO_ORGANIZATION }} - - - name: Setup .NET 6 - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 6.0.x - - - name: Install dependencies - run: dotnet restore --runtime ${{ matrix.runtime }} - env: - ADO_TOKEN: ${{ steps.getToken.outputs.token }} - - name: Test - run: dotnet test --no-restore --configuration release - - - name: Build artifacts - run: dotnet publish src/AzureAuth/AzureAuth.csproj -p:Version=${{ github.event.inputs.version }} --configuration release --self-contained true --runtime ${{ matrix.runtime }} --output dist/${{ matrix.runtime }} - env: - ADO_TOKEN: ${{ steps.getToken.outputs.token }} - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-${{ matrix.runtime }} - path: dist/${{ matrix.runtime }} - - analyze: - runs-on: ubuntu-latest - permissions: - security-events: write - id-token: write - needs: [validate] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Get Azure DevOps Access Token - id: getToken - uses: "./.github/actions/get-ado-token" - with: - client-id: ${{ secrets.AZURE_RELEASE_WORKFLOW_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_RELEASE_WORKFLOW_TENANT_ID }} - organization: ${{ secrets.ADO_ORGANIZATION }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: csharp - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - env: - ADO_TOKEN: ${{ steps.getToken.outputs.token }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:csharp" - - sign: - # This step has to run on Windows because ESRPClient.exe is currently only available for that platform. - runs-on: windows-latest - needs: [build, analyze] - strategy: - matrix: - runtime: [osx-x64, osx-arm64, win10-x64] - permissions: - id-token: write - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Setup NuGet - uses: NuGet/setup-nuget@v1 - with: - nuget-version: '5.x' - - name: Get Azure DevOps Access Token - id: getToken - uses: "./.github/actions/get-ado-token" - with: - client-id: ${{ secrets.AZURE_RELEASE_WORKFLOW_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_RELEASE_WORKFLOW_TENANT_ID }} - organization: ${{ secrets.ADO_ORGANIZATION }} - - name: Download ESRPClient.exe - env: - ESRP_VERSION: ${{ secrets.ESRP_VERSION }} - NUGET_CREDENTIALS: ${{ steps.getToken.outputs.token }} - run: | - nuget sources add -Name esrp -Username esrp-downloader -Password $env:NUGET_CREDENTIALS -Source https://pkgs.dev.azure.com/office/_packaging/Office/nuget/v3/index.json - nuget install Microsoft.EsrpClient -Version "$env:ESRP_VERSION" -OutputDirectory .\esrp -Source https://pkgs.dev.azure.com/office/_packaging/Office/nuget/v3/index.json - - name: Login to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - # We need these certificates installed so that we can run ESRPClient.exe. - - name: Install certificates - env: - AZURE_SUBSCRIPTION: ${{ secrets.AZURE_SUBSCRIPTION }} - AZURE_VAULT: ${{ secrets.AZURE_VAULT }} - ESRP_AAD_CERT_NAME: ${{ secrets.AZURE_VAULT_ESRP_AAD_CERT_NAME }} - ESRP_REQ_CERT_NAME: ${{ secrets.AZURE_VAULT_ESRP_REQ_CERT_NAME }} - run: | - az keyvault secret download --subscription "$env:AZURE_SUBSCRIPTION" --vault-name "$env:AZURE_VAULT" --name "$env:ESRP_AAD_CERT_NAME" -f cert.pfx - certutil -f -importpfx cert.pfx - Remove-Item cert.pfx - - az keyvault secret download --subscription "$env:AZURE_SUBSCRIPTION" --vault-name "$env:AZURE_VAULT" --name "$env:ESRP_REQ_CERT_NAME" -f cert.pfx - certutil -f -importpfx cert.pfx - Remove-Item cert.pfx - # We download all artifacts and overwrite them with signed files, but only upload ones which we can properly sign. - - name: Download all artifacts - uses: actions/download-artifact@v3 - - name: Sign artifacts - env: - SIGNING_AAD_ID: ${{ secrets.SIGNING_AAD_ID }} - SIGNING_TENANT_ID: ${{ secrets.SIGNING_TENANT_ID }} - SIGNING_KEY_CODE_AUTHENTICODE: ${{ secrets.SIGNING_KEY_CODE_AUTHENTICODE }} - SIGNING_KEY_CODE_MAC: ${{ secrets.SIGNING_KEY_CODE_MAC }} - SIGNING_KEY_CODE_LINUX: ${{ secrets.SIGNING_KEY_CODE_LINUX }} - SIGNING_CUSTOMER_CORRELATION_ID: ${{ secrets.SIGNING_CUSTOMER_CORRELATION_ID }} - ESRP_CLIENT_EXE: ".\\esrp\\Microsoft.EsrpClient.${{ secrets.ESRP_VERSION }}\\tools\\EsrpClient.exe" - run: python .\bin\sign.py "$env:ESRP_CLIENT_EXE" --runtime=${{ matrix.runtime }} --source=azureauth-${{ github.event.inputs.version }}-${{ matrix.runtime }} - - name: Upload signed artifacts - uses: actions/upload-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-${{ matrix.runtime }} - path: azureauth-${{ github.event.inputs.version }}-${{ matrix.runtime }} - - - # Currently we package artifacts into the most commonly accessible archive format for their respective platforms. - package: - runs-on: ubuntu-latest - needs: [sign] - steps: - - name: Download all artifacts - uses: actions/download-artifact@v3 - - name: Install Zip - run: sudo apt install -y zip - - name: Create win10-x64 archive - run: | - cd azureauth-${{ github.event.inputs.version }}-win10-x64 - zip ../azureauth-${{ github.event.inputs.version }}-win10-x64.zip * - - name: Upload win10-x64 artifact - uses: actions/upload-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-win10-x64.zip - path: azureauth-${{ github.event.inputs.version }}-win10-x64.zip - - name: Create osx-x64 archive - run: | - cd azureauth-${{ github.event.inputs.version }}-osx-x64 - chmod +x azureauth createdump *.dylib - tar -czf ../azureauth-${{ github.event.inputs.version }}-osx-x64.tar.gz * - - name: Upload osx-x64 artifact - uses: actions/upload-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-osx-x64.tar.gz - path: azureauth-${{ github.event.inputs.version }}-osx-x64.tar.gz - - name: Create osx-arm64 archive - run: | - cd azureauth-${{ github.event.inputs.version }}-osx-arm64 - chmod +x azureauth createdump *.dylib - tar -czf ../azureauth-${{ github.event.inputs.version }}-osx-arm64.tar.gz * - - name: Upload osx-arm64 artifact - uses: actions/upload-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-osx-arm64.tar.gz - path: azureauth-${{ github.event.inputs.version }}-osx-arm64.tar.gz - - release: - runs-on: ubuntu-latest - needs: [package] - # The 'release' environment is what requires reviews before creating the release. - environment: - name: release - # These permissions are required in order to use `softprops/action-gh-release` to upload. - permissions: - contents: write - steps: - - name: Download win10-x64 artifact - uses: actions/download-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-win10-x64.zip - - name: Download osx-x64 artifact - uses: actions/download-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-osx-x64.tar.gz - - name: Download osx-arm64 artifact - uses: actions/download-artifact@v3 - with: - name: azureauth-${{ github.event.inputs.version }}-osx-arm64.tar.gz - - - name: Create Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ github.event.inputs.version }} - body: "Release ${{ github.event.inputs.version }}. See [`CHANGELOG.md`](https://github.com/AzureAD/microsoft-authentication-cli/blob/${{ github.event.inputs.version }}/CHANGELOG.md) for updates." - tag_name: ${{ github.event.inputs.version }} - prerelease: ${{ github.event.inputs.prerelease }} - files: | - azureauth-${{ github.event.inputs.version }}-win10-x64.zip - azureauth-${{ github.event.inputs.version }}-osx-x64.tar.gz - azureauth-${{ github.event.inputs.version }}-osx-arm64.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index 1caba211..9ffa9326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed - Temporarily paused the publishing of Linux binaries. +- Upgrade MSAL from `4.59.1` to `4.61.3`. ## [0.8.6] - 2024-04-25 ### Changed diff --git a/bin/mac/publish b/bin/mac/publish index de326b85..9e8536ee 100755 --- a/bin/mac/publish +++ b/bin/mac/publish @@ -42,4 +42,4 @@ esac #------------------------------------------ # Publish -dotnet publish "$GIT_DIR/src/AzureAuth/AzureAuth.csproj" --self-contained true -r "$runtime" -c release -o $DIST $* +$GIT_DIR/bin/mac/dotnet publish "$GIT_DIR/src/AzureAuth/AzureAuth.csproj" --self-contained true -r "$runtime" -c release -o $DIST $* diff --git a/bin/requirements.txt b/bin/requirements.txt index d245284e..9150348e 100644 --- a/bin/requirements.txt +++ b/bin/requirements.txt @@ -1,11 +1,11 @@ azure-devops==6.0.0b4 -certifi==2023.7.22 +certifi==2024.7.4 charset-normalizer==2.1.1 idna==3.7 isodate==0.6.1 msrest==0.6.21 oauthlib==3.2.2 -requests==2.31.0 +requests==2.32.0 requests-oauthlib==1.3.1 six==1.16.0 -urllib3==1.26.18 \ No newline at end of file +urllib3==1.26.19 \ No newline at end of file diff --git a/bin/sign.py b/bin/sign.py deleted file mode 100644 index 2b48ad78..00000000 --- a/bin/sign.py +++ /dev/null @@ -1,367 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -"""A script which wraps ESRPClient.exe for code signing.""" - -import json -import os -import subprocess -import sys -from argparse import ArgumentParser -from argparse import ArgumentDefaultsHelpFormatter -from argparse import Namespace -from collections.abc import Iterator -from collections.abc import Generator -from contextlib import ExitStack -from contextlib import contextmanager -from pathlib import Path -from typing import Any -from zipfile import ZipFile -from zipfile import ZIP_DEFLATED - -JSON = dict[str, Any] # A naive type alias for JSON. - - -def sign_operation(key_code: str, operation: str) -> JSON: - """Return the JSON signing operation for a given key code/operation.""" - return { - "KeyCode": key_code, - "OperationCode": operation, - "Parameters": { - "OpusName": "Microsoft", - "OpusInfo": "http://www.microsoft.com", - "FileDigest": '/fd "SHA256"', - "PageHash": "/NPH", - "TimeStamp": '/tr "http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer" /td sha256', - }, - "ToolName": "sign", - "ToolVersion": "1.0", - } - - -def sign_operation_linux(key_code: str, operation: str) -> JSON: - return { - "KeyCode": key_code, - "OperationCode": operation, - "Parameters": {}, - "ToolName": "sign", - "ToolVersion": "1.0", - } - - -def linux_sign(key_code: str) -> JSON: - """Return the JSON for a `LinuxSign` operation.""" - return sign_operation_linux(key_code, operation="LinuxSign") - - -def mac_app_developer_sign(key_code: str) -> JSON: - """Return the JSON for a `MacAppDeveloperSign` operation.""" - return sign_operation(key_code, operation="MacAppDeveloperSign") - - -def sign_tool_sign(key_code: str) -> JSON: - """Return the JSON for a `SigntoolSign` operation.""" - return sign_operation(key_code, operation="SigntoolSign") - - -def sign_tool_verify(key_code: str) -> JSON: - """Return the JSON for a `SigntoolVerify` operation.""" - return { - "KeyCode": key_code, - "OperationCode": "SigntoolVerify", - "Parameters": {}, - "ToolName": "sign", - "ToolVersion": "1.0", - } - - -def sign_request_file(source: Path, customer_correlation_id: str) -> JSON: - """Return the JSON for a `SignRequestFiles` entry.""" - return { - "CustomerCorrelationId": customer_correlation_id, - "SourceLocation": source.name, - "DestinationLocation": source.name, - } - - -def batch(source: Path, files: list[JSON], operations: list[JSON]) -> JSON: - """Return a single signing batch for a given set of files and operations.""" - return { - "SourceLocationType": "UNC", - "SourceRootDirectory": str(source), - "DestinationLocationType": "UNC", - "DestinationRootDirectory": str(source), - "SignRequestFiles": files, - "SigningInfo": {"Operations": operations}, - } - - -@contextmanager -def windows_batches( - source: Path, - key_codes: dict[str, str], - customer_correlation_id: str, -) -> Generator[JSON, None, None]: - """Yield the JSON signing batches for the win-x64 runtime.""" - files = [ - sign_request_file(path, customer_correlation_id) - for path in source.iterdir() - if path.suffix in [".exe", ".dll"] and path.is_file() - ] - - key_code = key_codes["authenticode"] - operations = [sign_tool_sign(key_code), sign_tool_verify(key_code)] - - # Yield the batches to ESRPClient.exe signing. - yield { - "Version": "1.0.0", - "SignBatches": [batch(source, files, operations)], - } - - -@contextmanager -def osx_batches( - source: Path, - key_codes: dict[str, str], - customer_correlation_id: str, -) -> Generator[JSON, None, None]: - """Yield the JSON signing batches for the osx-x64 and osx-arm64 runtimes.""" - dlls = [] - dylibs = [] - dylibs_zip = source / "dylibs.zip" - - # Find .dlls and .dylibs (including azureauth). - for path in source.iterdir(): - if path.suffix == ".dll" and path.is_file(): - dlls.append(path) - elif (path.name == "azureauth" or path.suffix == ".dylib") and path.is_file(): - dylibs.append(path) - - with ZipFile(dylibs_zip, mode="w", compression=ZIP_DEFLATED) as file: - for path in dylibs: - file.write(path, path.relative_to(source)) - - dll_files = [sign_request_file(dll, customer_correlation_id) for dll in dlls] - dylib_files = [sign_request_file(dylibs_zip, customer_correlation_id)] - - authenticode_key_code = key_codes["authenticode"] - mac_key_code = key_codes["mac"] - dll_operations = [ - sign_tool_sign(authenticode_key_code), - sign_tool_verify(authenticode_key_code), - ] - dylib_operations = [mac_app_developer_sign(mac_key_code)] - - # Yield the batches to ESRPClient.exe signing. - yield { - "Version": "1.0.0", - "SignBatches": [ - batch(source, dll_files, dll_operations), - batch(source, dylib_files, dylib_operations), - ], - } - - # At this point signing is finished. Extract the signed dylibs. - with ZipFile(dylibs_zip, mode="r") as file: - file.extractall(source) - dylibs_zip.unlink() - - -@contextmanager -def linux_batches( - source: Path, - key_codes: dict[str, str], - customer_correlation_id: str, -) -> Generator[JSON, None, None]: - """Yield the JSON signing batches for the linux-x64 runtime.""" - files = [ - sign_request_file(path, customer_correlation_id) - for path in source.iterdir() - if path.suffix in [".deb"] and path.is_file() - ] - - key_code = key_codes["linux"] - operations = [linux_sign(key_code)] - - # Yield the batches to ESRPClient.exe signing. - yield { - "Version": "1.0.0", - "SignBatches": [batch(source, files, operations)], - } - - -def auth(tenant_id: str, client_id: str) -> JSON: - """Return auth JSON metadata.""" - return { - "Version": "1.0.0", - "AuthenticationType": "AAD_CERT", - "TenantId": tenant_id, - "ClientId": client_id, - "AuthCert": { - "SubjectName": f"CN={client_id}.microsoft.com", - "StoreLocation": "LocalMachine", - "StoreName": "My", - "SendX5c": "true", - }, - "RequestSigningCert": { - "SubjectName": f"CN={client_id}", - "StoreLocation": "LocalMachine", - "StoreName": "My", - }, - } - - -def policy() -> JSON: - """Return policy JSON metadata.""" - return { - "Version": "1.0.0", - "Intent": "Product Release", - "ContentType": "Signed Binaries", - } - - -@contextmanager -def json_tempfile(path: Path, data: JSON) -> Generator[None, None, None]: - """Create a JSON file with the given data and later remove it.""" - with path.open(mode="w") as file: - json.dump(obj=data, fp=file, indent=2) - yield - path.unlink() - - -def parse_env_vars(runtime: str) -> tuple[str, str, str, JSON]: - """Parse and return environment variables""" - try: - aad_id = os.environ["SIGNING_AAD_ID"] - tenant_id = os.environ["SIGNING_TENANT_ID"] - customer_correlation_id = os.environ["SIGNING_CUSTOMER_CORRELATION_ID"] - match runtime: - case "win10-x64": - # This key code is used for signing .exes and .dlls on both Windows and Mac. - key_codes = { - "authenticode": os.environ["SIGNING_KEY_CODE_AUTHENTICODE"] - } - case "osx-x64" | "osx-arm64": - # SIGNING_KEY_CODE_AUTHENTICODE is used for signing .exes and .dlls on both Windows and Mac. - # SIGNING_KEY_CODE_MAC is used for signing .dylibs on Macs. - key_codes = { - "authenticode": os.environ["SIGNING_KEY_CODE_AUTHENTICODE"], - "mac": os.environ["SIGNING_KEY_CODE_MAC"], - } - case "linux-x64" | "linux-arm64": - # This key code is used for signing .deb on Linux. - key_codes = {"linux": os.environ["SIGNING_KEY_CODE_LINUX"]} - - return aad_id, tenant_id, customer_correlation_id, key_codes - except KeyError as exc: - # See https://stackoverflow.com/a/24999035/3288364. - name = str(exc).replace("'", "") - raise KeyError(f"Error: missing env var: {name}") - - -def parse_args() -> Namespace: - """Parse and return command line arguments.""" - cwd = Path.cwd() - parser = ArgumentParser( - description=__doc__, - formatter_class=ArgumentDefaultsHelpFormatter, - ) - - parser.add_argument( - "esrp_client", - help="the path to the ESRPClient.exe binary", - type=Path, - ) - parser.add_argument( - "--source", - metavar="SRC", - help="the source path", - type=Path, - default=str(cwd), - ) - parser.add_argument( - "--runtime", - choices=["win10-x64", "osx-x64", "osx-arm64", "linux-x64"], - help="the runtime of the build in source", - default="win10-x64", - ) - - return parser.parse_args() - - -def main() -> None: - """Determine target runtime, generate inputs, and run ESRPClient.exe.""" - # 1. Parse command line arguments. - args = parse_args() - runtime = args.runtime.lower() - - # 2. Read env vars. - aad_id, tenant_id, customer_correlation_id, key_codes = parse_env_vars(runtime) - - esrp_path = args.esrp_client.resolve() - source_path = args.source.resolve() - auth_path = Path("auth.json").resolve() - policy_path = Path("policy.json").resolve() - input_path = Path("input.json").resolve() - output_path = Path("output.json").resolve() - - # 3. Determine runtime & create a batchmaker. - match runtime: - case "win10-x64": - batchmaker = windows_batches( - source=source_path, - key_codes=key_codes, - customer_correlation_id=customer_correlation_id, - ) - case "osx-x64" | "osx-arm64": - batchmaker = osx_batches( - source=source_path, - key_codes=key_codes, - customer_correlation_id=customer_correlation_id, - ) - case "linux-x64": - batchmaker = linux_batches( - source=source_path, - key_codes=key_codes, - customer_correlation_id=customer_correlation_id, - ) - case _: - # This should be unreachable because of argparse, but let's be safe. - sys.exit(f"Error: Invalid runtime: {args.runtime}") - - # 4. Create the necessary context and run ESRPClient. - esrp_args = [ - str(esrp_path), - "sign", - "-a", - str(auth_path), - "-i", - str(input_path), - "-p", - str(policy_path), - "-o", - str(output_path), - "-l", - "Progress", - ] - - # All temporary files created in this context should be cleaned up. - with ExitStack() as stack: - # Generate auth.json. - auth_json = auth(tenant_id, aad_id) - stack.enter_context(json_tempfile(auth_path, auth_json)) - - # Generate policy.json. - policy_json = policy() - stack.enter_context(json_tempfile(policy_path, policy_json)) - - # Generate input.json (and any supporting intermediate files). - batches = stack.enter_context(batchmaker) - stack.enter_context(json_tempfile(input_path, batches)) - - # Run ESRPClient.exe. - subprocess.run(esrp_args, check=True) - - -if __name__ == "__main__": - main() diff --git a/src/AdoPat/AdoPat.csproj b/src/AdoPat/AdoPat.csproj index 88deddb7..5e615c97 100644 --- a/src/AdoPat/AdoPat.csproj +++ b/src/AdoPat/AdoPat.csproj @@ -6,14 +6,18 @@ Microsoft.Authentication.AdoPat - + - + + + + + diff --git a/src/MSALWrapper.Benchmark/BrokerBenchmark.cs b/src/MSALWrapper.Benchmark/BrokerBenchmark.cs index b986b6ef..5c030136 100644 --- a/src/MSALWrapper.Benchmark/BrokerBenchmark.cs +++ b/src/MSALWrapper.Benchmark/BrokerBenchmark.cs @@ -44,7 +44,6 @@ public BrokerBenchmark() public void WarmUp() { NativeBrokerBenchmark(); - ManagedBrokerBenchmark(); } /// @@ -53,27 +52,14 @@ public void WarmUp() [Benchmark] public void NativeBrokerBenchmark() { - var pcaWrapper = BuildPCAWrapper(this.logger, this.clientID, this.tenantID, true); + var pcaWrapper = BuildPCAWrapper(this.logger, this.clientID, this.tenantID); AuthParameters authParameters = new AuthParameters(this.clientID, this.tenantID, this.scopes); Broker broker = new Broker(this.logger, authParameters, pcaWrapper: pcaWrapper); broker.GetTokenAsync().Wait(); } - /// - /// Benchmark with .Net managed broker. - /// - [Benchmark] - public void ManagedBrokerBenchmark() - { - var pcaWrapper = BuildPCAWrapper(this.logger, this.clientID, this.tenantID, false); - AuthParameters authParameters = new AuthParameters(this.clientID, this.tenantID, this.scopes); - Broker broker = new Broker(this.logger, authParameters, pcaWrapper: pcaWrapper); - - broker.GetTokenAsync().Wait(); - } - - private IPCAWrapper BuildPCAWrapper(ILogger logger, Guid clientId, Guid tenantId, bool useNativeBroker) + private IPCAWrapper BuildPCAWrapper(ILogger logger, Guid clientId, Guid tenantId) { IList errors = new List(); @@ -87,14 +73,7 @@ private IPCAWrapper BuildPCAWrapper(ILogger logger, Guid clientId, Guid tenantId enablePiiLogging: false, enableDefaultPlatformLogging: true) ; - if (useNativeBroker) - { - clientBuilder.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows)); - } - else - { - clientBuilder.WithBroker(); - } + clientBuilder.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows)); return new PCAWrapper(logger, clientBuilder.Build(), errors, tenantId); } diff --git a/src/MSALWrapper.Benchmark/MSALWrapper.Benchmark.csproj b/src/MSALWrapper.Benchmark/MSALWrapper.Benchmark.csproj index 2652e4db..2e2c3892 100644 --- a/src/MSALWrapper.Benchmark/MSALWrapper.Benchmark.csproj +++ b/src/MSALWrapper.Benchmark/MSALWrapper.Benchmark.csproj @@ -21,7 +21,7 @@ - + @@ -31,7 +31,7 @@ - 4.59.1 + 4.61.3 @@ -41,6 +41,6 @@ - + diff --git a/src/MSALWrapper/MSALWrapper.csproj b/src/MSALWrapper/MSALWrapper.csproj index cc2fed03..9a3229d8 100644 --- a/src/MSALWrapper/MSALWrapper.csproj +++ b/src/MSALWrapper/MSALWrapper.csproj @@ -30,10 +30,10 @@ - + - - + +