├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── workflows │ ├── build-docker.yml │ ├── codeql-analysis.yml │ ├── maven.yml │ ├── publish-docker.yml │ └── release.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── README.md ├── SECURITY.md ├── docs ├── CEF-connectivity.md ├── cef-connectivity │ └── index.md ├── img │ ├── cef_static_discovery_form.PNG │ ├── peppol_testbed1.PNG │ ├── peppol_testbed2.PNG │ ├── peppol_testbed3.PNG │ ├── peppol_testbed4.PNG │ ├── peppol_testbed5.PNG │ ├── peppol_testbed6.PNG │ ├── peppol_testbed7.PNG │ ├── peppol_testbed8.PNG │ ├── tomcat_folder.PNG │ └── tomcat_oxalis_folder.PNG ├── installation │ ├── index.md │ ├── server.md │ ├── standalone.md │ └── tomcat.md └── peppol-test-bed │ └── index.md ├── hooks └── post_push ├── pom.xml ├── pull_request_template.md └── src ├── main ├── assembly │ ├── assembly-dist-zip.xml │ └── assembly-dist.xml ├── java │ ├── network │ │ └── oxalis │ │ │ └── as4 │ │ │ ├── api │ │ │ └── MessageIdGenerator.java │ │ │ ├── common │ │ │ ├── AS4Constants.java │ │ │ ├── As4CommonModule.java │ │ │ ├── As4MessageProperties.java │ │ │ ├── As4MessageProperty.java │ │ │ ├── DefaultMessageIdGenerator.java │ │ │ ├── DummyHeaderParser.java │ │ │ └── MerlinProvider.java │ │ │ ├── config │ │ │ └── As4Conf.java │ │ │ ├── inbound │ │ │ ├── AS4MessageContextKey.java │ │ │ ├── AS4StatusServlet.java │ │ │ ├── AbstractSetPolicyInterceptor.java │ │ │ ├── As4EndpointSelector.java │ │ │ ├── As4EndpointsPublisher.java │ │ │ ├── As4EndpointsPublisherImpl.java │ │ │ ├── As4EnvelopeHeader.java │ │ │ ├── As4FaultInHandler.java │ │ │ ├── As4InboundHandler.java │ │ │ ├── As4InboundMetadata.java │ │ │ ├── As4InboundModule.java │ │ │ ├── As4Interceptor.java │ │ │ ├── As4PayloadHeader.java │ │ │ ├── As4Provider.java │ │ │ ├── As4Servlet.java │ │ │ ├── AttachmentCleanupInterceptor.java │ │ │ ├── MessagingHandler.java │ │ │ ├── OxalisAS4Version.java │ │ │ ├── ProsessingContext.java │ │ │ ├── SetPolicyInInterceptor.java │ │ │ └── SetPolicyOutInterceptor.java │ │ │ ├── lang │ │ │ ├── AS4Error.java │ │ │ ├── OxalisAs4Exception.java │ │ │ └── OxalisAs4TransmissionException.java │ │ │ ├── outbound │ │ │ ├── ActionProvider.java │ │ │ ├── As4MessageSender.java │ │ │ ├── As4MessageSenderFacade.java │ │ │ ├── As4OutboundModule.java │ │ │ ├── As4TransmissionRequest.java │ │ │ ├── As4TransmissionResponse.java │ │ │ ├── BrowserTypeProvider.java │ │ │ ├── DefaultActionProvider.java │ │ │ ├── LoggingBeforeSecurityInInterceptor.java │ │ │ ├── MessagingProvider.java │ │ │ └── TransmissionResponseConverter.java │ │ │ └── util │ │ │ ├── AS4ErrorCode.java │ │ │ ├── As4MessageFactory.java │ │ │ ├── CompressionUtil.java │ │ │ ├── Constants.java │ │ │ ├── GeneralUtils.java │ │ │ ├── InputStreamDataSource.java │ │ │ ├── Marshalling.java │ │ │ ├── MessageId.java │ │ │ ├── MessageIdUtil.java │ │ │ ├── OxalisAlgorithmSuiteLoader.java │ │ │ ├── PeppolConfiguration.java │ │ │ ├── PolicyService.java │ │ │ ├── SOAPHeaderParser.java │ │ │ ├── TransmissionRequestUtil.java │ │ │ └── XMLUtil.java │ └── org │ │ └── apache │ │ └── cxf │ │ └── attachment │ │ ├── As4AttachmentDataSource.java │ │ ├── As4AttachmentDeserializer.java │ │ ├── As4AttachmentImpl.java │ │ ├── As4AttachmentInInterceptor.java │ │ ├── As4AttachmentUtil.java │ │ ├── As4DelegatingInputStream.java │ │ └── As4LazyAttachmentCollection.java ├── resources │ ├── META-INF │ │ └── javax.xml.ws.spi.Provider │ ├── eDeliveryAS4Policy.xml │ ├── eDeliveryAS4Policy_BST.xml │ ├── oxalis-as4-version.properties │ ├── reference.conf │ └── signOnly.xml └── xsd │ ├── bindings.xml │ ├── ebxml │ ├── ebbp-signals-2.0.xsd │ └── ebms-header-3_0-200704.xsd │ ├── w3 │ ├── XMLSchema.dtd │ ├── datatypes.dtd │ ├── soap-envelope.xsd │ ├── xlink.xsd │ ├── xml.xsd │ └── xmldsig-core-schema.xsd │ └── xmlsoap │ └── envelope.xsd └── test ├── java └── network │ └── oxalis │ ├── as4 │ ├── SendReceiveTest.java │ ├── common │ │ └── DefaultMessageIdGeneratorTest.java │ ├── inbound │ │ ├── AS4StatusServletTest.java │ │ ├── As4InboundHandlerTest.java │ │ └── As4ServletTest.java │ ├── outbound │ │ ├── AbstractMessagingProviderTest.java │ │ ├── MessagingProviderTest_CEF_SBDH.java │ │ └── MessagingProviderTest_SIMPLE_SBDH.java │ └── util │ │ ├── AS4ErrorCodeTest.java │ │ ├── CompressionUtilTest.java │ │ ├── MessageIdUtilTest.java │ │ └── TransmissionRequestUtilTest.java │ └── outbound │ └── transmission │ ├── DefaultTransmissionRequestFacade.java │ └── MessagingProviderFacade.java └── resources ├── as2-peppol-bis-invoice-sbdh.xml ├── cef-sbd.xml ├── logback-test.xml ├── oxalis_home ├── eutest_gateway_truststore.jks ├── fake-oxalis.conf └── peppol_trust_g2_and_g3.jks ├── reference.conf └── simple-sbd.xml /.dockerignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a bug report to help us improve 3 | labels: 4 | - kind/bug 5 | - status/0-triage 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for taking the time to report a bug... 11 | If this is a security issue please report it to the [oxalis@norstella.no](mailto:oxalis@norstella.no). 12 | - type: textarea 13 | id: description 14 | attributes: 15 | label: Description 16 | description: Please give a clear and concise description of the bug 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: repro 21 | attributes: 22 | label: Reproduce 23 | description: Please list steps to reproduce the bug 24 | placeholder: | 25 | 1. ... 26 | 2. ... 27 | 3. ... 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: expected 32 | attributes: 33 | label: Expected behavior 34 | description: What is the expected behavior? 35 | placeholder: | 36 | E.g. "...." 37 | - type: textarea 38 | id: Oxalis-AS4-version 39 | attributes: 40 | label: Oxalis-AS4 version 41 | description: Mention Oxalis-AS4 version 42 | render: bash 43 | placeholder: | 44 | E.g. "...." 45 | validations: 46 | required: true 47 | - type: textarea 48 | id: JDK-version 49 | attributes: 50 | label: JDK version 51 | description: JDK version where Oxalis-AS4 is running 52 | render: bash 53 | placeholder: | 54 | E.g. "...." 55 | validations: 56 | required: true 57 | - type: textarea 58 | id: additional 59 | attributes: 60 | label: Additional Info 61 | description: Additional info you want to provide such as logs, system info, environment, etc. 62 | validations: 63 | required: false 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Security and Vulnerabilities 4 | url: https://github.com/OxalisCommunity/oxalis-as4/blob/master/SECURITY.md 5 | about: Please report any security issues or vulnerabilities responsibly to the Oxalis Community team. Please do not use the public issue tracker. 6 | - name: Questions and Discussions 7 | url: https://github.com/OxalisCommunity/oxalis-as4/discussions/new 8 | about: Use Github Discussions to ask questions and/or open discussion topics. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Missing functionality? Come tell us about it! 3 | labels: 4 | - kind/feature 5 | - status/0-triage 6 | body: 7 | - type: textarea 8 | id: problem-description 9 | attributes: 10 | label: Description 11 | description: A clear and concise description of the feature you want to see? 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: alternative-considered 16 | attributes: 17 | label: Description considered, if any? 18 | description: A clear and concise description of any alternative solutions or features you've considered. 19 | validations: 20 | required: false 21 | - type: textarea 22 | id: additional-context 23 | attributes: 24 | label: Additional context 25 | description: Additional info you want to provide. 26 | validations: 27 | required: false -------------------------------------------------------------------------------- /.github/workflows/build-docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Docker hub 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | 11 | - name: Set up QEMU 12 | uses: docker/setup-qemu-action@v1 13 | with: 14 | platforms: all 15 | 16 | - name: Set up Docker Buildx 17 | id: buildx 18 | uses: docker/setup-buildx-action@v1 19 | 20 | - name: Cache Docker layers 21 | uses: actions/cache@v4 22 | with: 23 | path: /tmp/.buildx-cache 24 | key: ${{ runner.os }}-buildx-${{ github.sha }} 25 | restore-keys: | 26 | ${{ runner.os }}-buildx- 27 | - name: Login to DockerHub 28 | if: github.event_name != 'pull_request' 29 | uses: docker/login-action@v1 30 | with: 31 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 32 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 33 | 34 | - name: Build and push 35 | id: docker_build 36 | uses: docker/build-push-action@v2 37 | with: 38 | builder: ${{ steps.buildx.outputs.name }} 39 | context: ./ 40 | file: ./Dockerfile 41 | platforms: linux/amd64,linux/arm64/v8 42 | push: true 43 | tags: norstella/oxalis-as4:7.2.0-RC4-latest 44 | cache-from: type=local,src=/tmp/.buildx-cache 45 | cache-to: type=local,dest=/tmp/.buildx-cache 46 | 47 | - name: Image digest 48 | run: echo ${{ steps.docker_build.outputs.digest }} 49 | 50 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | 13 | name: "CodeQL Analyss for Oxalis AS4" 14 | 15 | on: 16 | push: 17 | branches: [ "master" ] 18 | pull_request: 19 | # The branches below must be a subset of the branches above 20 | branches: [ "master" ] 21 | schedule: 22 | - cron: '19 6 * * 0' 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'java' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 38 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | 53 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 54 | # queries: security-extended,security-and-quality 55 | 56 | 57 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 58 | # If this step fails, then you should remove it and run the build manually (see below) 59 | - name: Autobuild 60 | uses: github/codeql-action/autobuild@v2 61 | 62 | # Command-line programs to run using the OS shell. 63 | # See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 64 | 65 | # If the Autobuild fails above, remove it and uncomment the following three lines. 66 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 67 | 68 | # - run: | 69 | # echo "Run, Build Application using script" 70 | # ./location_of_script_within_repo/buildscript.sh 71 | 72 | - name: Perform CodeQL Analysis 73 | uses: github/codeql-action/analyze@v2 74 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Oxalis-AS4 Master Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set up JDK 11 11 | uses: actions/setup-java@v4 12 | with: 13 | distribution: 'temurin' 14 | java-version: '11' 15 | - name: Build with Maven 16 | run: mvn -B --no-transfer-progress package --file pom.xml 17 | -------------------------------------------------------------------------------- /.github/workflows/publish-docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish Releases to Docker hub 2 | 3 | # When its time to do a release do a full cross platform build for all supported 4 | # architectures and push all of them to Docker Hub. 5 | # Only trigger on semver shaped tags. 6 | on: 7 | push: 8 | tags: 9 | - "v*.*.*" 10 | 11 | jobs: 12 | docker: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Prepare 18 | id: prep 19 | run: | 20 | DOCKER_IMAGE=norstella/oxalis-as4 21 | VERSION=edge 22 | if [[ $GITHUB_REF == refs/tags/* ]]; then 23 | VERSION=${GITHUB_REF#refs/tags/v} 24 | fi 25 | if [ "${{ github.event_name }}" = "schedule" ]; then 26 | VERSION=nightly 27 | fi 28 | TAGS="${DOCKER_IMAGE}:${VERSION}" 29 | if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 30 | TAGS="$TAGS,${DOCKER_IMAGE}:latest" 31 | fi 32 | echo ::set-output name=tags::${TAGS} 33 | 34 | - name: Set up QEMU 35 | uses: docker/setup-qemu-action@v1 36 | with: 37 | platforms: all 38 | 39 | - name: Set up Docker Buildx 40 | id: buildx 41 | uses: docker/setup-buildx-action@v1 42 | 43 | - name: Cache Docker layers 44 | uses: actions/cache@v4 45 | with: 46 | path: /tmp/.buildx-cache 47 | key: ${{ runner.os }}-buildx-${{ github.sha }} 48 | restore-keys: | 49 | ${{ runner.os }}-buildx- 50 | - name: Login to DockerHub 51 | if: github.event_name != 'pull_request' 52 | uses: docker/login-action@v1 53 | with: 54 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 55 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 56 | 57 | - name: Build and push 58 | id: docker_build 59 | uses: docker/build-push-action@v2 60 | with: 61 | builder: ${{ steps.buildx.outputs.name }} 62 | context: ./ 63 | file: ./Dockerfile 64 | platforms: linux/amd64,linux/arm64/v8 65 | push: true 66 | tags: norstella/oxalis-as4:7.2.0-RC4 67 | cache-from: type=local,src=/tmp/.buildx-cache 68 | cache-to: type=local,dest=/tmp/.buildx-cache 69 | 70 | - name: Image digest 71 | run: echo ${{ steps.docker_build.outputs.digest }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Oxalis-AS4 Release publish 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set up JDK 11 11 | uses: actions/setup-java@v4 12 | with: 13 | distribution: 'temurin' 14 | java-version: '11' 15 | server-id: central 16 | server-username: MAVEN_USERNAME 17 | server-password: MAVEN_PASSWORD 18 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 19 | gpg-passphrase: GPG_PASSPHRASE 20 | - name: Publish to the Maven Central Repository 21 | run: mvn --batch-mode deploy -P release-sign-artifacts 22 | env: 23 | MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} 24 | MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} 25 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | oxalis-as4.iml 4 | output.mime 5 | /qodana.yaml -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to Oxalis contributing guide 2 | 3 | Thank you for investing your time in contributing to our project !!! 4 | Any contribution you make will be reflected on [Oxalis-AS4 Releases](https://github.com/OxalisCommunity/oxalis-as4/releases) :sparkles:. 5 | 6 | Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. 7 | In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. 8 | 9 | ## New contributor guide 10 | 11 | To get an overview of the project, read the [README](./README.md) file. Here are some resources to help you get started with open source contributions: 12 | 13 | - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) 14 | - [Set up Git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) 15 | - [GitHub flow](https://docs.github.com/en/get-started/using-github/github-flow) 16 | - [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) 17 | 18 | 19 | ## Getting started 20 | 21 | Before starting contributing, please make yourself aware of codebase, and also go through WIKI pages. 22 | 23 | ### Issues 24 | 25 | #### Create a new issue 26 | 27 | If you found a bug/issue, before creating [issue](https://github.com/OxalisCommunity/oxalis-as4/issues) first search if such issue already exist or not - Search in both open and [closed](https://github.com/OxalisCommunity/oxalis-as4/issues?q=is%3Aissue%20state%3Aclosed) issues. 28 | If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/OxalisCommunity/oxalis-as4/issues/new/choose). 29 | 30 | #### Solve an issue 31 | 32 | Scan through our [existing issue](https://github.com/OxalisCommunity/oxalis-as4/issues) to find one that interests you. You can narrow down the search using `labels` as filters. 33 | As a general rule, we don’t assign issues to anyone. If you find an issue to work on, you are welcome to open a PR with a fix. 34 | 35 | ### Make Changes 36 | 37 | #### Make changes locally 38 | 39 | 1. Fork the repository. 40 | - Using GitHub Desktop: 41 | - [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop. 42 | - Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)! 43 | 44 | - Using the command line: 45 | - [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them. 46 | 47 | 2. Create a working branch and start with your changes! 48 | 49 | ### Pull Request 50 | 51 | When you're finished with the changes, create a pull request, also known as a PR - [Pull Request Template](https://github.com/OxalisCommunity/oxalis-as4/blob/master/pull_request_template.md) 52 | 53 | ### Your PR is merged! 54 | 55 | Congratulations :tada::tada: The Oxalis Community team thanks you :sparkles:. 56 | 57 | Once your PR is merged, your contributions will be publicly visible on the [Oxalis Releases](https://github.com/OxalisCommunity/oxalis-as4/releases). 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.8.6-jdk-11 AS mvn 2 | 3 | ADD . $MAVEN_HOME 4 | 5 | RUN cd $MAVEN_HOME \ 6 | && mvn -B clean package -DskipTests=true \ 7 | && cp -r target/$(ls target | grep "\-dist$" | head -1) /dist 8 | 9 | 10 | FROM norstella/oxalis:7.2.0-RC4 11 | 12 | COPY --from=mvn /dist /oxalis/ext -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Oxalis-AS4 Master Build](https://github.com/OxalisCommunity/Oxalis-AS4/workflows/Oxalis-AS4%20Master%20Build/badge.svg)](https://github.com/OxalisCommunity/oxalis-as4/actions?query=workflow%3A%22oxalis-as4%20Master%20Build%22) 2 | [![Maven Central](https://img.shields.io/maven-central/v/network.oxalis/oxalis-as4.svg)](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22network.oxalis%22%20AND%20a%3A%22oxalis-as4%22) 3 | --- 4 | # Upcoming Migration Notice: Deprecation of Current Repository 5 | Please be advised that this GitHub repository will no longer be supported after **December 31, 2025**. We strongly encourage all Oxalis users and contributors to begin planning their upgrade to **[Oxalis-NG](https://github.com/OxalisCommunity/oxalis-ng)**. 6 | 7 | --- 8 | # Oxalis-AS4 9 | This repo implement PEPPOL AS4 pMode. 10 | It supports Oxalis version v6.0.0 and above 11 | 12 | AS4 messages is triggered by setting the _transport profile identifier_ of one of your endpoints to "_peppol-transport-as4-v2_0_" in the SMP. No further configuration is needed beyond the standard Oxalis setup. 13 | For general instructions on how to install and use Oxalis, please refer to [oxalis installation guide](https://github.com/difi/oxalis/blob/master/doc/installation.md). 14 | 15 | * [Installation guide](docs/installation/index.md) 16 | * [OpenPEPPOL Test Bed](docs/peppol-test-bed/index.md) 17 | * [CEF connectivity test](docs/cef-connectivity/index.md) 18 | 19 | --- 20 | # Are you Contributor? 21 | We are actively looking for contributors who can contribute to Oxalis-AS4 and associated Git repositories. You can start fixing issues by selecting any existing issue or you can add new feature. Please refer [Pull request Checklist](/pull_request_template.md) while generating new pull request. Team will review your code, if it will meet desired goal, and will be according to standards and guidelines then it will be merged to master. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The maintainers of the Oxalis Community take security seriously. If you discover a security issue, please bring it to their attention right away! 4 | 5 | ## Reporting a Vulnerability 6 | 7 | Please **DO NOT** file a public issue, instead send your report privately to [oxalis@norstella.no](mailto:oxalis@norstella.no). 8 | Reporter(s) can expect a response within 72 hours, acknowledging the issue was received. 9 | 10 | ## Review Process 11 | 12 | After receiving the report, an initial triage and technical analysis is performed to confirm the report and determine its scope. We may request additional information in this stage of the process. 13 | 14 | Once a reviewer has confirmed the relevance of the report, a draft security advisory will be created on GitHub. The draft advisory will be used to discuss the issue with maintainers, the reporter(s), and where applicable, other affected parties under embargo. 15 | 16 | If the vulnerability is accepted, a timeline for developing a patch, public disclosure, and patch release will be determined. If there is an embargo period on public disclosure before the patch release, the reporter(s) are expected to participate in the discussion of the timeline and abide by agreed upon dates 17 | for public disclosure. 18 | 19 | ## Accreditation 20 | 21 | Security reports are greatly appreciated and we will publicly thank you, although we will keep your name confidential if you request it. We do not currently offer a paid security bounty program at this time. 22 | -------------------------------------------------------------------------------- /docs/img/cef_static_discovery_form.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/cef_static_discovery_form.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed1.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed2.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed3.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed4.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed5.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed6.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed7.PNG -------------------------------------------------------------------------------- /docs/img/peppol_testbed8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/peppol_testbed8.PNG -------------------------------------------------------------------------------- /docs/img/tomcat_folder.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/tomcat_folder.PNG -------------------------------------------------------------------------------- /docs/img/tomcat_oxalis_folder.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/docs/img/tomcat_oxalis_folder.PNG -------------------------------------------------------------------------------- /docs/installation/index.md: -------------------------------------------------------------------------------- 1 | # Oxalis AS4 installation 2 | 3 | We will be basing this installation guide on Oxalis _5.0.5_ for Oxalis and _5.0.3_ for Oxalis-AS4. Specifically _Oxalis Server_ ([download](https://search.maven.org/artifact/network.oxalis/oxalis-server/5.0.5/jar)) for inbound traffic and _Oxalis Standalone_ ([download](https://search.maven.org/artifact/network.oxalis/oxalis-standalone/5.0.5/jar)) for outbound traffic. The same approach will work for all the other components of Oxalis 5.x 4 | 5 | ## Additional/Alternate download links: 6 | Here are the links to download Oxalis Artifacts maven repositories: 7 | - https://search.maven.org/search?q=g:network.oxalis (Sonatype: Maven Central Repository Search) OR 8 | - https://repo1.maven.org/maven2/network/oxalis/ (Maven Repo) OR 9 | - https://mvnrepository.com/artifact/network.oxalis (Mvn Repository) 10 | 11 | Depending upon how you are using/integrated those artifacts, you can download them GitHub Releases section: 12 | - https://github.com/OxalisCommunity/oxalis/releases/tag/v5.0.5 13 | - https://github.com/OxalisCommunity/Oxalis-AS4/releases/tag/v5.0.3 14 | 15 | 16 | # Inbound 17 | * [Oxalis Inbound (Server)](server.md) 18 | * [Oxalis Inbound (Tomcat 8+)](tomcat.md) 19 | * [Oxalis Inbound (Docker)](https://hub.docker.com/r/norstella/oxalis-as4/) 20 | 21 | # Outbound 22 | * [Oxalis Outbound (Standalone)](standalone.md) 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/installation/server.md: -------------------------------------------------------------------------------- 1 | ### Install Oxalis Inbound (Server) 2 | 3 | Oxalis server comes out of the box with a folder for extensions (named "ext"). Extract the content of _oxalis-as4-6.0.0-dist.zip_ into this folder. No further configuration is needed. 4 | 5 | Start Oxalis server in the normal way, either trough _run.sh_ or _run.bat_. 6 | 7 | The easiest way to see that the AS4 endpoint is up and running is to visit its endpoint address. 8 | If we now visit ``localhost:8080/as4`` we will be greeted with the message ``Hello AS4 world`` -------------------------------------------------------------------------------- /docs/installation/standalone.md: -------------------------------------------------------------------------------- 1 | ### Install Oxalis Outbound (Standalone) 2 | 3 | Oxalis SimpleSender does not come with an extension folder. So we need to add the extension logic that where define in the _run_ scripts our self. 4 | 5 | We make a base folder named oxalis-standalone-as4 with two sub folders: 6 |
 7 | oxalis-standalone-as4/    <-- Base folder, we will run our commands from here
 8 | ├── standalone/    <-- We will putt our regular Oxalis Standalone application here...
 9 | │   ├── oxalis-standalone.jar
10 | │   ├── posibly-other.jar
11 | │   └── ...
12 | └── as4/    <-- ...and our AS4 extension here
13 |     ├── oxalis-as4.jar
14 |     ├── many-other.jar
15 |     └── ...
16 | 
17 | 18 | To run our combined application all we need to do is to run the following command (This command assumes we are standing in our base folder): 19 |
20 | java -classpath "standalone/*;as4/*" eu.sendregning.oxalis.Main [followd by the argument like -f c:\some-invoice.xml]
21 | 
22 | 23 | All this command does is to tell Java to load the content of both folders, then execute the logic in "_eu.sendregning.oxalis.Main_" (which is the starting point of the Standalone application). 24 | By looking into the run scripts of Oxalis Server form our previous section we can see that this is in fact the same approach that is used there. 25 | -------------------------------------------------------------------------------- /docs/installation/tomcat.md: -------------------------------------------------------------------------------- 1 | # Install Oxalis Inbound (Tomcat 8+) 2 | 3 | ## Oxalis 4 | 5 | First you should download the [oxalis-war-6.0.0.war](https://github.com/OxalisCommunity/oxalis/releases/download/oxalis-6.0.0/oxalis-war-6.0.0.war) file 6 | from this [page](https://github.com/OxalisCommunity/oxalis/releases) and 7 | put it a directory of your choice. We recommend naming the folder oxalis. 8 | 9 | Then you should download the [Oxalis-AS4 distribution](https://github.com/OxalisCommunity/oxalis-as4/releases/download/6.0.0/oxalis-as4-6.0.0-dist.zip) 10 | from this [page](https://github.com/OxalisCommunity/oxalis-as4/releases) and unzip 11 | the files to a new folder of your choice. We recommend creating a folder named as4 inside the folder containing the WAR file. 12 | 13 | Also create a folder named home inside the oxalis folder and place the oxalis.conf file, together with the necessary JKS files. 14 | 15 | Then you should have something looking like this like this: 16 | 17 | ![Oxalis folder](../img/tomcat_oxalis_folder.PNG "Oxalis folder") 18 | 19 | For general instructions on how to install and use Oxalis, please refer to [oxalis installation guide](https://github.com/difi/oxalis/blob/master/doc/installation.md). 20 | 21 | ## Tomcat installation 22 | 23 | First you need to [download](https://tomcat.apache.org/download-90.cgi) Tomcat and unzip the files to a directory. 24 | 25 | ![Tomcat folder](../img/tomcat_folder.PNG "Tomcat folder") 26 | 27 | Then you should set the CATALINA_BASE environment variable to the chosen installation folder. 28 | 29 | ## Tomcat configuration for Oxalis 30 | 31 | The next step is to create a folder named Catalina in the Tomcat conf directory. 32 | 33 | ```bash 34 | cd conf 35 | mkdir Catalina 36 | ``` 37 | 38 | And inside the newly created Catalina folder - create a folder named localhost. 39 | 40 | ```bash 41 | cd Catalina 42 | mkdir localhost 43 | ``` 44 | 45 | Inside this localhost folder - create a file named oxalis.xml containing: 46 | 47 | ```xml 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 74 | 75 | 76 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | ``` 90 | 91 | This is only an example deployment XML file for tomcat 8+. Ensure that your file locations match with the paths in the XML. 92 | 93 | Now you are ready to start the Tomcat server. 94 | 95 | # Verifying the installation 96 | 97 | The easiest way to see that the AS4 endpoint is up and running is to visit its endpoint address. 98 | If we now visit ``localhost:8080/oxalis/as4`` we will be greeted with the message ``Hello AS4 world`` 99 | 100 | -------------------------------------------------------------------------------- /hooks/post_push: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$SOURCE_BRANCH" = "master" ]; then 4 | exit 5 | fi 6 | 7 | docker tag $IMAGE_NAME $DOCKER_REPO:latest 8 | docker push $DOCKER_REPO:latest 9 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Pull Request Description 2 | 3 | Please include a summary of the change request or bug which is supposed to be fixed as part of this pull request 4 | 5 | ## Type of Pull Request 6 | 7 | - [ ] New feature/Enhancement - non-breaking change which adds functionality 8 | - [ ] Bug fix 9 | - [ ] Breaking change (Require Major version change?) 10 | 11 | ## Type of Change 12 | 13 | - [ ] OpenPeppol AS4 specification 14 | - [ ] Oxalis software change or enhancement 15 | - [ ] CEF change 16 | 17 | ## Pull Request Checklist: 18 | 19 | - [ ] My code follows the style guidelines of this project 20 | - [ ] I have commented my code, particularly in hard-to-understand areas. But did not added unnecessary annotation/comment say @author name etc 21 | - [ ] I have checked my code for variable and method name and corrected grammar/spelling mistakes if any 22 | - [ ] I have made corresponding changes to the documentation where needed 23 | - [ ] My changes generate no new/additional warnings 24 | - [ ] My change is not breaking or creating conflict with associated dependencies 25 | - [ ] I have performed a self-review of my own code 26 | - [ ] I ran `mvn clean install` before commit and all tests run successfully 27 | - [ ] I conducted basic QA to assure all features are working fine 28 | - [ ] My pull request generate no conflicts with `master` branch 29 | - [ ] I requested code review from other team members 30 | -------------------------------------------------------------------------------- /src/main/assembly/assembly-dist-zip.xml: -------------------------------------------------------------------------------- 1 | 2 | dist 3 | 4 | dir 5 | tar.gz 6 | zip 7 | 8 | false 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/assembly/assembly-dist.xml: -------------------------------------------------------------------------------- 1 | 2 | dist 3 | 4 | dir 5 | tar.gz 6 | zip 7 | 8 | false 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/api/MessageIdGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.api; 24 | 25 | public interface MessageIdGenerator { 26 | 27 | String generate(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/AS4Constants.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.common; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | @UtilityClass 6 | public final class AS4Constants { 7 | 8 | public static final String PEPPOL = "peppol"; 9 | public static final String CEF_CONNECTIVITY = "cef-connectivity"; 10 | public static final String CEF_CONFORMANCE = "cef-conformance"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/As4CommonModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.common; 24 | 25 | import com.google.inject.Injector; 26 | import com.google.inject.Provides; 27 | import com.google.inject.Singleton; 28 | import lombok.extern.slf4j.Slf4j; 29 | import network.oxalis.as4.api.MessageIdGenerator; 30 | import network.oxalis.as4.outbound.DefaultActionProvider; 31 | import network.oxalis.as4.util.OxalisAlgorithmSuiteLoader; 32 | import network.oxalis.as4.util.PolicyService; 33 | import network.oxalis.as4.util.TransmissionRequestUtil; 34 | import network.oxalis.api.header.HeaderParser; 35 | import network.oxalis.api.settings.Settings; 36 | import network.oxalis.as4.config.As4Conf; 37 | import network.oxalis.as4.outbound.ActionProvider; 38 | import network.oxalis.as4.util.As4MessageFactory; 39 | import network.oxalis.commons.guice.ImplLoader; 40 | import network.oxalis.commons.guice.OxalisModule; 41 | import network.oxalis.vefa.peppol.mode.Mode; 42 | import org.apache.cxf.Bus; 43 | import org.apache.cxf.BusFactory; 44 | import org.apache.cxf.transport.http.HttpServerEngineSupport; 45 | import org.apache.wss4j.dom.engine.WSSConfig; 46 | 47 | import java.security.Security; 48 | 49 | import static network.oxalis.as4.common.AS4Constants.*; 50 | 51 | @Slf4j 52 | public class As4CommonModule extends OxalisModule { 53 | 54 | @Override 55 | protected void configure() { 56 | bindTyped(MessageIdGenerator.class, DefaultMessageIdGenerator.class); 57 | bindTyped(HeaderParser.class, DummyHeaderParser.class); 58 | bind(As4MessageFactory.class); 59 | bindSettings(As4Conf.class); 60 | bind(MerlinProvider.class); 61 | 62 | Bus bus = BusFactory.newInstance().createBus(); 63 | bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true); 64 | new OxalisAlgorithmSuiteLoader(bus); 65 | BusFactory.setThreadDefaultBus(bus); 66 | 67 | Security.setProperty("jdk.security.provider.preferred", "AES/GCM/NoPadding:BC"); 68 | WSSConfig.init(); 69 | } 70 | 71 | @Provides 72 | @Singleton 73 | public MessageIdGenerator getMessageIdGenerator(Injector injector, Settings settings) { 74 | return ImplLoader.get(injector, MessageIdGenerator.class, settings, As4Conf.MSGID_GENERATOR); 75 | } 76 | 77 | @Provides 78 | @Singleton 79 | public PolicyService getPolicyService(Mode mode, Settings settings, ActionProvider actionProvider) { 80 | String type = settings.getString(As4Conf.TYPE); 81 | 82 | if (Mode.PRODUCTION.equals(mode.getIdentifier()) && !PEPPOL.equals(type)) { 83 | throw new IllegalStateException("oxalis.as4.type has to be peppol in PRODUCTION!"); 84 | } 85 | 86 | if (CEF_CONNECTIVITY.equalsIgnoreCase(type)) { 87 | return new PolicyService(actionProvider) { 88 | @Override 89 | protected String getDefaultPolicy() { 90 | return "/eDeliveryAS4Policy.xml"; 91 | } 92 | }; 93 | } else if (CEF_CONFORMANCE.equalsIgnoreCase(type)) { 94 | return new PolicyService(actionProvider) { 95 | 96 | @Override 97 | protected String getPolicyClasspath(String action, String service) { 98 | log.debug("Service = {}, Action = {}", service, action); 99 | 100 | if ("SRV_ONEWAY_SIGNONLY".equals(service) 101 | && "busdox-docid-qns::ACT_ONEWAY_SIGNONLY".equals(action)) { 102 | return "/signOnly.xml"; 103 | } 104 | 105 | return getDefaultPolicy(); 106 | } 107 | 108 | 109 | @Override 110 | protected String getDefaultPolicy() { 111 | return "/eDeliveryAS4Policy.xml"; 112 | } 113 | }; 114 | } 115 | 116 | return new PolicyService(actionProvider); 117 | } 118 | 119 | @Provides 120 | @Singleton 121 | public ActionProvider getActionProvider(Settings settings) { 122 | String type = settings.getString(As4Conf.TYPE); 123 | if (CEF_CONNECTIVITY.equalsIgnoreCase(type)) { 124 | return p -> { 125 | String action = TransmissionRequestUtil.translateDocumentTypeToAction(p); 126 | 127 | if (action.startsWith("connectivity::cef##connectivity::")) { 128 | return action.replaceFirst("connectivity::cef##connectivity::", ""); 129 | } 130 | 131 | return action; 132 | }; 133 | } else if (CEF_CONFORMANCE.equalsIgnoreCase(type)) { 134 | return p -> { 135 | if ("http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/test".equals(p.getIdentifier())) { 136 | return "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/test"; 137 | } 138 | 139 | return TransmissionRequestUtil.translateDocumentTypeToAction(p); 140 | }; 141 | } 142 | 143 | return new DefaultActionProvider(); 144 | } 145 | } -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/As4MessageProperties.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.common; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class As4MessageProperties extends ArrayList { 6 | 7 | public boolean isMissing(String name) { 8 | return stream().noneMatch(p -> name.equals(p.getName())); 9 | } 10 | 11 | public String getValueByName(String name) { 12 | return stream().filter(p -> name.equals(p.getName())).findAny().map(As4MessageProperty::getValue).orElse(null); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/As4MessageProperty.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.common; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Value; 5 | 6 | @Value 7 | @EqualsAndHashCode 8 | public class As4MessageProperty { 9 | 10 | String name; 11 | String type; 12 | String value; 13 | 14 | public As4MessageProperty(String name, String value) { 15 | this(name, null, value); 16 | } 17 | 18 | public As4MessageProperty(String name, String type, String value) { 19 | this.name = name; 20 | this.type = type; 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/DefaultMessageIdGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.common; 24 | 25 | import com.google.inject.Inject; 26 | import com.google.inject.Singleton; 27 | import lombok.SneakyThrows; 28 | import network.oxalis.as4.api.MessageIdGenerator; 29 | import network.oxalis.api.settings.Settings; 30 | import network.oxalis.api.util.Type; 31 | import network.oxalis.as4.config.As4Conf; 32 | 33 | import java.net.InetAddress; 34 | import java.net.UnknownHostException; 35 | import java.util.UUID; 36 | 37 | @Singleton 38 | @Type("default") 39 | public class DefaultMessageIdGenerator implements MessageIdGenerator { 40 | 41 | private final String hostname; 42 | 43 | public DefaultMessageIdGenerator(String hostname) { 44 | this.hostname = hostname; 45 | } 46 | 47 | @Inject 48 | public DefaultMessageIdGenerator(Settings settings) { 49 | this.hostname = getHostname(settings); 50 | } 51 | 52 | private String getHostname(Settings settings) { 53 | String name = settings.getString(As4Conf.HOSTNAME).trim(); 54 | return name.isEmpty() ? getLocalHostName() : name; 55 | } 56 | 57 | @SneakyThrows(UnknownHostException.class) 58 | private String getLocalHostName() { 59 | return InetAddress.getLocalHost().getCanonicalHostName(); 60 | } 61 | 62 | @Override 63 | public String generate() { 64 | return String.format("%s@%s", UUID.randomUUID().toString(), hostname); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/DummyHeaderParser.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.common; 2 | 3 | import com.google.inject.Singleton; 4 | import lombok.extern.slf4j.Slf4j; 5 | import network.oxalis.api.header.HeaderParser; 6 | import network.oxalis.api.util.Type; 7 | import network.oxalis.vefa.peppol.common.model.*; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.Date; 12 | 13 | @Slf4j 14 | @Singleton 15 | @Type("dummy") 16 | public class DummyHeaderParser implements HeaderParser { 17 | 18 | @Override 19 | public Header parse(InputStream inputStream) { 20 | 21 | log.debug("DummyHeaderParser: parse"); 22 | 23 | try { 24 | byte[] drain = new byte[500]; 25 | inputStream.read(drain); 26 | } catch (IOException e) { 27 | log.error("IOException while parsing header", e); 28 | } 29 | 30 | return Header.of( 31 | ParticipantIdentifier.of("DummySender"), 32 | ParticipantIdentifier.of("DummyReceiver"), 33 | ProcessIdentifier.of("DummyProcess"), 34 | DocumentTypeIdentifier.of("DummyDocument"), 35 | C1CountryIdentifier.of("DummyCountry"), 36 | MlsToIdentifier.of("DummyMlsTo"), 37 | MlsTypeIdentifier.of("DummyMlsType"), 38 | InstanceIdentifier.of("DummyInstance"), 39 | InstanceType.of("Dummy", "InstanceType", "1.0"), 40 | new Date(0L)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/common/MerlinProvider.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.common; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.google.inject.name.Named; 6 | import lombok.extern.slf4j.Slf4j; 7 | import network.oxalis.api.lang.OxalisLoadingException; 8 | import network.oxalis.vefa.peppol.mode.Mode; 9 | import org.apache.wss4j.common.crypto.Merlin; 10 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.security.KeyStore; 17 | import java.security.KeyStoreException; 18 | import java.security.NoSuchAlgorithmException; 19 | import java.security.cert.CertificateException; 20 | import java.util.Enumeration; 21 | import java.util.Optional; 22 | 23 | @Slf4j 24 | @Singleton 25 | public class MerlinProvider { 26 | 27 | @Inject 28 | private Mode mode; 29 | 30 | @Inject 31 | @Named("conf") 32 | private Path confFolder; 33 | 34 | @Inject 35 | private KeyStore keyStore; 36 | 37 | @Inject(optional = true) 38 | @Named("truststore-ap") 39 | private KeyStore trustStoreAp; 40 | 41 | private KeyStore cachedTrustStore; 42 | 43 | public Merlin getMerlin() { 44 | Merlin merlin = new Merlin(); 45 | merlin.setCryptoProvider(BouncyCastleProvider.PROVIDER_NAME); 46 | merlin.setKeyStore(keyStore); 47 | merlin.setTrustStore(getCachedTrustStore()); 48 | return merlin; 49 | } 50 | 51 | private KeyStore getCachedTrustStore() { 52 | if (cachedTrustStore == null) { 53 | cachedTrustStore = getTrustStore(); 54 | } 55 | 56 | return cachedTrustStore; 57 | } 58 | 59 | private KeyStore getTrustStore() { 60 | Optional trustStoreExtension = loadTrustStoreApFromConf(mode, confFolder); 61 | 62 | if (trustStoreAp != null) { 63 | trustStoreExtension.ifPresent(p -> 64 | extendKeyStore(trustStoreAp, p) 65 | ); 66 | 67 | return trustStoreAp; 68 | } 69 | 70 | return trustStoreExtension 71 | .orElseThrow(() -> new OxalisLoadingException("Expected a truststore. Please specify the property security.truststore.ap")); 72 | } 73 | 74 | private void extendKeyStore(KeyStore trustStoreAp, KeyStore trustStoreExtension) { 75 | try { 76 | Enumeration aliases = trustStoreExtension.aliases(); 77 | while (aliases.hasMoreElements()) { 78 | String alias = aliases.nextElement(); 79 | if (!trustStoreAp.containsAlias(alias)) { 80 | log.info("Adding {} to truststore", alias); 81 | trustStoreAp.setCertificateEntry(alias, trustStoreExtension.getCertificate(alias)); 82 | } 83 | } 84 | } catch (KeyStoreException e) { 85 | throw new OxalisLoadingException("Something went wrong during extension of key store.", e); 86 | } 87 | } 88 | 89 | private Optional loadTrustStoreApFromConf(Mode mode, Path confFolder) { 90 | String truststoreAp = mode.getString("security.truststore.ap"); 91 | 92 | if (truststoreAp == null) { 93 | return Optional.empty(); 94 | } 95 | 96 | Path path = confFolder.resolve(truststoreAp); 97 | 98 | try { 99 | KeyStore keystore = KeyStore.getInstance("JKS"); 100 | if (!path.toFile().exists()) return Optional.empty(); 101 | 102 | log.info("Loading TRUSTSTORE: {}", path); 103 | 104 | try (InputStream inputStream = Files.newInputStream(path)) { 105 | keystore.load(inputStream, mode.getString("security.truststore.password").toCharArray()); 106 | } 107 | return Optional.of(keystore); 108 | } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) { 109 | throw new OxalisLoadingException("Something went wrong during handling of key store.", e); 110 | } catch (IOException e) { 111 | throw new OxalisLoadingException(String.format("Error during reading of '%s'.", path), e); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/config/As4Conf.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.config; 24 | 25 | import network.oxalis.api.settings.DefaultValue; 26 | import network.oxalis.api.settings.Path; 27 | import network.oxalis.api.settings.Title; 28 | 29 | @Title("AS4") 30 | public enum As4Conf { 31 | 32 | @Path("oxalis.as4.hostname") 33 | @DefaultValue("") 34 | HOSTNAME, 35 | 36 | @Path("oxalis.as4.msgidgen") 37 | @DefaultValue("default") 38 | MSGID_GENERATOR, 39 | 40 | @Path("oxalis.as4.type") 41 | @DefaultValue("peppol") 42 | TYPE 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/AS4MessageContextKey.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | @UtilityClass 6 | public class AS4MessageContextKey { 7 | 8 | public static final String FIRST_PAYLOAD_PATH = "network.oxalis.as4.first.payload.path"; 9 | public static final String FIRST_PAYLOAD_HEADER = "network.oxalis.as4.first.payload.header"; 10 | public static final String ENVELOPE_HEADER = "network.oxalis.as4.envelope.header"; 11 | public static final String PERSISTED = "network.oxalis.as4.persisted"; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/AS4StatusServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2018 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.inbound; 24 | 25 | import com.google.inject.Inject; 26 | import com.google.inject.Singleton; 27 | import network.oxalis.vefa.peppol.mode.Mode; 28 | 29 | import jakarta.servlet.http.HttpServlet; 30 | import jakarta.servlet.http.HttpServletRequest; 31 | import jakarta.servlet.http.HttpServletResponse; 32 | import java.io.IOException; 33 | import java.io.PrintWriter; 34 | 35 | /** 36 | * Servlet returning diagnostic information to ease operation, support and debugging. 37 | * Since this servlet is public accessible, it should NOT contain any sensitive 38 | * information about it's runtime environment. 39 | */ 40 | @Singleton 41 | public class AS4StatusServlet extends HttpServlet { 42 | 43 | private final Mode mode; 44 | 45 | @Inject 46 | public AS4StatusServlet(Mode mode) { 47 | this.mode = mode; 48 | } 49 | 50 | @Override 51 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { 52 | resp.setContentType("text/plain"); 53 | 54 | PrintWriter writer = resp.getWriter(); 55 | writer.println("version.oxalis.as4: " + OxalisAS4Version.getVersion()); 56 | writer.println("version.java: " + System.getProperty("java.version")); 57 | writer.println("mode: " + mode.getIdentifier()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/AbstractSetPolicyInterceptor.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import network.oxalis.as4.util.Constants; 5 | import network.oxalis.as4.util.Marshalling; 6 | import network.oxalis.as4.util.PolicyService; 7 | import org.apache.cxf.binding.soap.SoapMessage; 8 | import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor; 9 | import org.apache.cxf.headers.Header; 10 | import org.apache.cxf.interceptor.Fault; 11 | import org.apache.cxf.message.Message; 12 | import org.apache.cxf.ws.policy.PolicyConstants; 13 | import org.apache.neethi.Policy; 14 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Messaging; 15 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.UserMessage; 16 | import org.w3c.dom.Node; 17 | 18 | import jakarta.xml.bind.JAXBContext; 19 | import jakarta.xml.bind.JAXBException; 20 | import jakarta.xml.bind.Unmarshaller; 21 | import java.util.Collection; 22 | import java.util.Optional; 23 | import java.util.stream.Stream; 24 | 25 | import static org.apache.cxf.ws.security.SecurityConstants.USE_ATTACHMENT_ENCRYPTION_CONTENT_ONLY_TRANSFORM; 26 | 27 | @Slf4j 28 | abstract class AbstractSetPolicyInterceptor extends AbstractSoapInterceptor { 29 | 30 | private final JAXBContext jaxbContext = Marshalling.getInstance(); 31 | private final PolicyService policyService; 32 | 33 | public AbstractSetPolicyInterceptor(String phase, PolicyService policyService) { 34 | super(phase); 35 | this.policyService = policyService; 36 | } 37 | 38 | @Override 39 | public void handleMessage(SoapMessage message) throws Fault { 40 | message.put(USE_ATTACHMENT_ENCRYPTION_CONTENT_ONLY_TRANSFORM, true); 41 | 42 | Optional userMessage = getMessaging(message) 43 | .map(Messaging::getUserMessage) 44 | .map(Collection::stream).orElseGet(Stream::empty) 45 | .findFirst(); 46 | 47 | try { 48 | Policy policy = userMessage.isPresent() 49 | ? policyService.getPolicy(userMessage.get().getCollaborationInfo()) 50 | : policyService.getPolicy(); 51 | message.put(PolicyConstants.POLICY_OVERRIDE, policy); 52 | } catch (Exception e) { 53 | throw new Fault(e); 54 | } 55 | } 56 | 57 | private Optional getMessaging(Message message) { 58 | SoapMessage soapMessage = (SoapMessage) message; 59 | Header header = soapMessage.getHeader(Constants.MESSAGING_QNAME); 60 | 61 | if (header == null) { 62 | return Optional.empty(); 63 | } 64 | 65 | try { 66 | Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 67 | Messaging messaging = unmarshaller.unmarshal((Node) header.getObject(), Messaging.class).getValue(); 68 | return Optional.of(messaging); 69 | } catch (JAXBException e) { 70 | throw new Fault(e); 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4EndpointSelector.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor; 4 | import org.apache.cxf.endpoint.Endpoint; 5 | import org.apache.cxf.message.Message; 6 | import org.apache.cxf.phase.Phase; 7 | import org.apache.cxf.wsdl.interceptors.AbstractEndpointSelectionInterceptor; 8 | 9 | import java.util.Set; 10 | 11 | public class As4EndpointSelector extends AbstractEndpointSelectionInterceptor { 12 | 13 | public static final String ENDPOINT_NAME = "Endpoint-name"; 14 | public static final String OXALIS_AS4_ENDPOINT_NAME = "Oxalis-AS4"; 15 | 16 | public As4EndpointSelector() { 17 | super(Phase.READ); 18 | getAfter().add(ReadHeadersInterceptor.class.getName()); 19 | } 20 | 21 | @Override 22 | protected Endpoint selectEndpoint(Message message, Set endpoints) { 23 | 24 | for (Endpoint endpoint : endpoints) { 25 | if (OXALIS_AS4_ENDPOINT_NAME.equals(endpoint.get(ENDPOINT_NAME))) { 26 | return endpoint; 27 | } 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4EndpointsPublisher.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import org.apache.cxf.Bus; 4 | import org.apache.cxf.jaxws.EndpointImpl; 5 | 6 | public interface As4EndpointsPublisher { 7 | 8 | EndpointImpl publish(Bus bus); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4EndpointsPublisherImpl.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import org.apache.cxf.attachment.As4AttachmentInInterceptor; 5 | import org.apache.cxf.Bus; 6 | import org.apache.cxf.binding.soap.SoapMessage; 7 | import org.apache.cxf.binding.soap.SoapVersion; 8 | import org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor; 9 | import org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor; 10 | import org.apache.cxf.binding.soap.interceptor.StartBodyInterceptor; 11 | import org.apache.cxf.ext.logging.LoggingFeature; 12 | import org.apache.cxf.interceptor.StaxInEndingInterceptor; 13 | import org.apache.cxf.interceptor.StaxInInterceptor; 14 | import org.apache.cxf.jaxws.EndpointImpl; 15 | import org.apache.cxf.jaxws.handler.soap.SOAPHandlerFaultInInterceptor; 16 | import org.apache.cxf.message.Message; 17 | import org.apache.cxf.transport.MultipleEndpointObserver; 18 | import org.apache.cxf.ws.policy.WSPolicyFeature; 19 | import org.apache.cxf.wsdl.interceptors.AbstractEndpointSelectionInterceptor; 20 | 21 | import jakarta.xml.ws.Endpoint; 22 | import java.util.Arrays; 23 | 24 | import static org.apache.cxf.ws.security.SecurityConstants.ENABLE_STREAMING_SECURITY; 25 | 26 | public class As4EndpointsPublisherImpl implements As4EndpointsPublisher { 27 | 28 | @Inject 29 | private As4Provider as4Provider; 30 | 31 | @Inject 32 | private AbstractEndpointSelectionInterceptor endpointSelector; 33 | 34 | @Inject 35 | private As4FaultInHandler as4FaultInHandler; 36 | 37 | @Inject 38 | private As4Interceptor oxalisAs4Interceptor; 39 | 40 | @Inject 41 | private SetPolicyInInterceptor setPolicyInInterceptor; 42 | 43 | @Inject 44 | private SetPolicyOutInterceptor setPolicyOutInterceptor; 45 | 46 | @Override 47 | public EndpointImpl publish(Bus bus) { 48 | EndpointImpl endpoint = (EndpointImpl) Endpoint.publish("/", as4Provider, 49 | new LoggingFeature(), 50 | new WSPolicyFeature()); 51 | 52 | endpoint.getServer().getEndpoint().put("allow-multiplex-endpoint", Boolean.TRUE); 53 | endpoint.getServer().getEndpoint().put(ENABLE_STREAMING_SECURITY, false); 54 | endpoint.getServer().getEndpoint() 55 | .put(As4EndpointSelector.ENDPOINT_NAME, As4EndpointSelector.OXALIS_AS4_ENDPOINT_NAME); 56 | 57 | endpoint.getBinding().setHandlerChain(Arrays.asList(as4FaultInHandler, new MessagingHandler())); 58 | endpoint.getInInterceptors().add(oxalisAs4Interceptor); 59 | endpoint.getInInterceptors().add(setPolicyInInterceptor); 60 | endpoint.getInInterceptors().add(new AttachmentCleanupInterceptor()); 61 | 62 | endpoint.getOutInterceptors().add(setPolicyOutInterceptor); 63 | endpoint.getInFaultInterceptors().add(setPolicyInInterceptor); 64 | endpoint.getOutFaultInterceptors().add(setPolicyOutInterceptor); 65 | 66 | MultipleEndpointObserver newMO = new MultipleEndpointObserver(bus) { 67 | @Override 68 | protected Message createMessage(Message message) { 69 | return new SoapMessage(message); 70 | } 71 | }; 72 | 73 | newMO.getBindingInterceptors().add(new As4AttachmentInInterceptor()); 74 | newMO.getBindingInterceptors().add(new StaxInInterceptor()); 75 | newMO.getBindingInterceptors().add(new StaxInEndingInterceptor()); 76 | newMO.getBindingInterceptors().add(new SOAPHandlerFaultInInterceptor(endpoint.getBinding())); 77 | newMO.getBindingInterceptors().add(new ReadHeadersInterceptor(bus, (SoapVersion) null)); 78 | newMO.getBindingInterceptors().add(new StartBodyInterceptor()); 79 | newMO.getBindingInterceptors().add(new CheckFaultInterceptor()); 80 | 81 | 82 | // Add in a default selection interceptor 83 | newMO.getRoutingInterceptors().add(endpointSelector); 84 | newMO.getEndpoints().add(endpoint.getServer().getEndpoint()); 85 | 86 | endpoint.getServer().getDestination().setMessageObserver(newMO); 87 | 88 | return endpoint; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4EnvelopeHeader.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import lombok.Data; 4 | import network.oxalis.as4.common.As4MessageProperties; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @Data 10 | public class As4EnvelopeHeader { 11 | 12 | private String messageId; 13 | private String conversationId; 14 | 15 | private List fromPartyId; 16 | private String fromPartyRole; 17 | 18 | private List toPartyId; 19 | private String toPartyRole; 20 | 21 | private String service; 22 | private String action; 23 | 24 | private As4MessageProperties messageProperties = new As4MessageProperties(); 25 | 26 | private List payloadCIDs = new ArrayList<>(); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4FaultInHandler.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.lang.AS4Error; 7 | import network.oxalis.as4.lang.OxalisAs4Exception; 8 | import network.oxalis.as4.util.AS4ErrorCode; 9 | import network.oxalis.as4.util.As4MessageFactory; 10 | import network.oxalis.as4.util.MessageId; 11 | import network.oxalis.api.model.TransmissionIdentifier; 12 | import network.oxalis.api.persist.PersisterHandler; 13 | import org.apache.cxf.interceptor.Fault; 14 | import org.apache.cxf.message.Exchange; 15 | import org.apache.cxf.message.Message; 16 | import org.apache.cxf.phase.PhaseInterceptorChain; 17 | import org.apache.wss4j.common.ext.WSSecurityException; 18 | 19 | import javax.xml.namespace.QName; 20 | import jakarta.xml.soap.SOAPMessage; 21 | import jakarta.xml.ws.WebServiceException; 22 | import jakarta.xml.ws.handler.MessageContext; 23 | import jakarta.xml.ws.handler.soap.SOAPHandler; 24 | import jakarta.xml.ws.handler.soap.SOAPMessageContext; 25 | import java.nio.file.Path; 26 | import java.util.Collections; 27 | import java.util.Optional; 28 | import java.util.Set; 29 | 30 | @Slf4j 31 | @Singleton 32 | public class As4FaultInHandler implements SOAPHandler { 33 | 34 | private final As4MessageFactory as4MessageFactory; 35 | private final PersisterHandler persisterHandler; 36 | 37 | private static final String CERTIFICATE_ERROR_MSG = "Cannot find key for certificate"; 38 | private static final String ERROR_CODE_FAILED_CHECK = "FAILED_CHECK"; 39 | private static final String FAULT_CODE_FAILED_CHECK = "FailedCheck"; 40 | private static final String PEPPOL_NOT_SERVICED = "PEPPOL:NOT_SERVICED"; 41 | 42 | @Inject 43 | public As4FaultInHandler(As4MessageFactory as4MessageFactory, PersisterHandler persisterHandler) { 44 | this.as4MessageFactory = as4MessageFactory; 45 | this.persisterHandler = persisterHandler; 46 | } 47 | 48 | @Override 49 | public Set getHeaders() { 50 | return Collections.emptySet(); 51 | } 52 | 53 | @Override 54 | public boolean handleMessage(SOAPMessageContext context) { 55 | return true; 56 | } 57 | 58 | @Override 59 | public boolean handleFault(SOAPMessageContext context) { 60 | String messageId = Optional.ofNullable((MessageId) context.get(MessageId.MESSAGE_ID)) 61 | .map(MessageId::getValue) 62 | .orElse(null); 63 | 64 | Exception exception = (Exception) context.get(Exception.class.getName()); 65 | 66 | if (exception == null) { 67 | return true; 68 | } 69 | 70 | log.info("handleFault for Exception", exception); 71 | 72 | AS4Error as4Error = toAS4Error(exception); 73 | 74 | handleAS4Error(context, messageId, as4Error); 75 | 76 | if (as4Error instanceof OxalisAs4Exception) { 77 | OxalisAs4Exception oxalisAs4Exception = (OxalisAs4Exception) as4Error; 78 | if (PEPPOL_NOT_SERVICED.equals(oxalisAs4Exception.getMessage())) { 79 | context.put(MessageContext.HTTP_RESPONSE_CODE, 200); 80 | context.setScope(MessageContext.HTTP_RESPONSE_CODE, MessageContext.Scope.APPLICATION); 81 | } 82 | } 83 | 84 | return true; 85 | } 86 | 87 | protected void handleAS4Error(SOAPMessageContext context, String messageId, AS4Error as4Error) { 88 | SOAPMessage errorMessage = as4MessageFactory.createErrorMessage(messageId, as4Error); 89 | context.setMessage(errorMessage); 90 | 91 | Path firstPayloadPath = (Path) context.get(AS4MessageContextKey.FIRST_PAYLOAD_PATH); 92 | As4PayloadHeader firstPayloadHeader = (As4PayloadHeader) context.get(AS4MessageContextKey.FIRST_PAYLOAD_HEADER); 93 | 94 | if (messageId != null && firstPayloadPath != null) { 95 | try { 96 | persisterHandler.persist(TransmissionIdentifier.of(messageId), firstPayloadHeader, firstPayloadPath, as4Error.getException()); 97 | } catch (Exception e) { 98 | log.error("Unable to persist exception", e); 99 | } 100 | } 101 | } 102 | 103 | @Override 104 | public void close(MessageContext context) { 105 | // Intentionally left empty 106 | } 107 | 108 | public static AS4Error toAS4Error(Throwable t) { 109 | // Is there a better way of getting the inMessage using JAX-WS? 110 | Optional faultMessage = Optional.ofNullable(PhaseInterceptorChain.getCurrentMessage()); 111 | Optional inMessage = faultMessage 112 | .map(Message::getExchange) 113 | .map(Exchange::getInMessage); 114 | 115 | if (t instanceof Fault) { 116 | Fault fault = (Fault) t; 117 | t = fault.getCause(); 118 | } 119 | 120 | if (t instanceof WebServiceException) { 121 | WebServiceException webServiceException = (WebServiceException) t; 122 | t = webServiceException.getCause(); 123 | } 124 | 125 | if (t instanceof WSSecurityException && inMessage.isPresent()) { 126 | 127 | boolean IsSecurityException = false; 128 | String detailSecurityExceptionMessage = ""; 129 | 130 | if (null != t.getMessage()) { 131 | detailSecurityExceptionMessage = t.getMessage(); 132 | } 133 | 134 | if (null != ((WSSecurityException) t).getErrorCode()) { 135 | String errorCode = ((WSSecurityException) t).getErrorCode().name(); 136 | IsSecurityException = errorCode.equalsIgnoreCase(ERROR_CODE_FAILED_CHECK); 137 | } 138 | 139 | if (null != ((WSSecurityException) t).getFaultCode()) { 140 | String faultCode = (null == ((WSSecurityException) t).getFaultCode().getLocalPart() ? "" : ((WSSecurityException) t).getFaultCode().getLocalPart()); 141 | IsSecurityException = faultCode.equalsIgnoreCase(FAULT_CODE_FAILED_CHECK); 142 | } 143 | 144 | if (IsSecurityException || detailSecurityExceptionMessage.equalsIgnoreCase(CERTIFICATE_ERROR_MSG)) { 145 | return new OxalisAs4Exception(PEPPOL_NOT_SERVICED, AS4ErrorCode.EBMS_0004, AS4ErrorCode.Severity.FAILURE); 146 | } 147 | 148 | boolean isCompressionError = (boolean) inMessage.get().getOrDefault("oxalis.as4.compressionErrorDetected", false); 149 | if (isCompressionError) { 150 | 151 | return new OxalisAs4Exception( 152 | "Content cannot be compressed after signature/encryption", AS4ErrorCode.EBMS_0303); 153 | } 154 | 155 | return new OxalisAs4Exception(t.getMessage(), t, AS4ErrorCode.EBMS_0009, AS4ErrorCode.Severity.ERROR); 156 | } 157 | 158 | if (t instanceof AS4Error) { 159 | return (AS4Error) t; 160 | } 161 | 162 | return new OxalisAs4Exception(t.getMessage(), t, AS4ErrorCode.EBMS_0004, AS4ErrorCode.Severity.ERROR); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4InboundMetadata.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import network.oxalis.api.inbound.InboundMetadata; 4 | import network.oxalis.api.model.TransmissionIdentifier; 5 | import network.oxalis.api.tag.Tag; 6 | import network.oxalis.api.timestamp.Timestamp; 7 | import network.oxalis.vefa.peppol.common.model.*; 8 | 9 | import java.security.cert.X509Certificate; 10 | import java.util.*; 11 | 12 | public class As4InboundMetadata implements InboundMetadata { 13 | 14 | private final TransmissionIdentifier transmissionIdentifier; 15 | private final String conversationId; 16 | private final Header header; 17 | private final Date timestamp; 18 | private final TransportProfile transportProfile; 19 | private final Digest digest; 20 | private final Receipt primaryReceipt; 21 | private final List receipts; 22 | private final X509Certificate certificate; 23 | private final As4EnvelopeHeader as4EnvelopeHeader; 24 | 25 | 26 | public As4InboundMetadata(TransmissionIdentifier transmissionIdentifier, String conversationId, Header header, Timestamp timestamp, 27 | TransportProfile transportProfile, Digest digest, X509Certificate certificate, 28 | byte[] primaryReceipt, As4EnvelopeHeader as4EnvelopeHeader) { 29 | this.transmissionIdentifier = transmissionIdentifier; 30 | this.conversationId = conversationId; 31 | this.header = header; 32 | this.timestamp = timestamp.getDate(); 33 | this.transportProfile = transportProfile; 34 | this.digest = digest; 35 | this.certificate = certificate; 36 | this.primaryReceipt = Receipt.of("message/disposition-notification", primaryReceipt); 37 | 38 | List receipts = new ArrayList<>(); 39 | receipts.add(this.primaryReceipt); 40 | if (timestamp.getReceipt().isPresent()) 41 | receipts.add(timestamp.getReceipt().get()); 42 | this.receipts = Collections.unmodifiableList(receipts); 43 | 44 | this.as4EnvelopeHeader = as4EnvelopeHeader; 45 | } 46 | 47 | @Override 48 | public X509Certificate getCertificate() { 49 | return certificate; 50 | } 51 | 52 | @Override 53 | public TransmissionIdentifier getTransmissionIdentifier() { 54 | return transmissionIdentifier; 55 | } 56 | 57 | public String getConversationId() { 58 | return conversationId; 59 | } 60 | 61 | @Override 62 | public Header getHeader() { 63 | return header; 64 | } 65 | 66 | @Override 67 | public Date getTimestamp() { 68 | return timestamp; 69 | } 70 | 71 | @Override 72 | public Digest getDigest() { 73 | return digest; 74 | } 75 | 76 | @Override 77 | public TransportProtocol getTransportProtocol() { 78 | return TransportProtocol.AS4; 79 | } 80 | 81 | @Override 82 | public TransportProfile getProtocol() { 83 | return transportProfile; 84 | } 85 | 86 | @Override 87 | public List getReceipts() { 88 | return receipts; 89 | } 90 | 91 | @Override 92 | public Receipt primaryReceipt() { 93 | return primaryReceipt; 94 | } 95 | 96 | @Override 97 | public Tag getTag() { 98 | return Tag.NONE; 99 | } 100 | 101 | public As4EnvelopeHeader getAs4EnvelopeHeader() { 102 | return as4EnvelopeHeader; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4InboundModule.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Key; 4 | import com.google.inject.name.Names; 5 | import com.google.inject.servlet.ServletModule; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.cxf.wsdl.interceptors.AbstractEndpointSelectionInterceptor; 8 | 9 | import jakarta.servlet.http.HttpServlet; 10 | 11 | @Slf4j 12 | public class As4InboundModule extends ServletModule { 13 | 14 | @Override 15 | protected void configureServlets() { 16 | bind(AbstractEndpointSelectionInterceptor.class).to(As4EndpointSelector.class); 17 | 18 | bind(Key.get(HttpServlet.class, Names.named("oxalis-as4"))) 19 | .to(As4Servlet.class) 20 | .asEagerSingleton(); 21 | 22 | bind(As4Provider.class); 23 | bind(As4EndpointsPublisher.class).to(As4EndpointsPublisherImpl.class); 24 | bind(As4InboundHandler.class); 25 | 26 | serve("/as4").with(Key.get(HttpServlet.class, Names.named("oxalis-as4"))); 27 | serve("/as4/status").with(AS4StatusServlet.class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4Interceptor.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.lang.OxalisAs4Exception; 7 | import network.oxalis.as4.util.Constants; 8 | import network.oxalis.as4.util.Marshalling; 9 | import network.oxalis.as4.util.MessageId; 10 | import network.oxalis.as4.util.PolicyService; 11 | import org.apache.cxf.binding.soap.SoapMessage; 12 | import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor; 13 | import org.apache.cxf.headers.Header; 14 | import org.apache.cxf.interceptor.Fault; 15 | import org.apache.cxf.message.Message; 16 | import org.apache.cxf.phase.Phase; 17 | import org.apache.cxf.ws.policy.AssertionInfoMap; 18 | import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JStaxInInterceptor; 19 | import org.apache.neethi.Policy; 20 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.CollaborationInfo; 21 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.MessageInfo; 22 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Messaging; 23 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.UserMessage; 24 | import org.w3c.dom.Node; 25 | 26 | import jakarta.xml.bind.JAXBContext; 27 | import jakarta.xml.bind.JAXBException; 28 | import jakarta.xml.bind.Unmarshaller; 29 | import java.util.Collection; 30 | import java.util.Optional; 31 | import java.util.stream.Stream; 32 | 33 | @Slf4j 34 | @Singleton 35 | public class As4Interceptor extends AbstractSoapInterceptor { 36 | 37 | private final JAXBContext jaxbContext = Marshalling.getInstance(); 38 | private final PolicyService policyService; 39 | 40 | @Inject 41 | public As4Interceptor(PolicyService policyService) { 42 | super(Phase.PRE_PROTOCOL); 43 | this.policyService = policyService; 44 | addBefore(PolicyBasedWSS4JStaxInInterceptor.class.getName()); 45 | } 46 | 47 | @Override 48 | public void handleMessage(SoapMessage message) throws Fault { 49 | Messaging messaging = getMessaging(message); 50 | 51 | storeMessageIdInContext(message, messaging); 52 | 53 | Optional userMessage = Optional.ofNullable(messaging) 54 | .map(Messaging::getUserMessage) 55 | .map(Collection::stream).orElseGet(Stream::empty) 56 | .findFirst(); 57 | 58 | try { 59 | Policy policy = userMessage.isPresent() 60 | ? policyService.getPolicy(userMessage.get().getCollaborationInfo()) 61 | : policyService.getPolicy(); 62 | message.put(AssertionInfoMap.class.getName(), new AssertionInfoMap(policy)); 63 | } catch (Exception e) { 64 | throw new Fault(e); 65 | } 66 | } 67 | 68 | private Messaging getMessaging(Message message) { 69 | SoapMessage soapMessage = (SoapMessage) message; 70 | Header header = soapMessage.getHeader(Constants.MESSAGING_QNAME); 71 | 72 | try { 73 | Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 74 | return unmarshaller.unmarshal((Node) header.getObject(), Messaging.class).getValue(); 75 | } catch (JAXBException e) { 76 | throw new Fault(e); 77 | } 78 | } 79 | 80 | private void storeMessageIdInContext(Message message, Messaging messaging) throws Fault { 81 | String messageId = Optional.ofNullable(messaging) 82 | .map(Messaging::getUserMessage) 83 | .map(Collection::stream).orElseGet(Stream::empty) 84 | .map(UserMessage::getMessageInfo) 85 | .map(MessageInfo::getMessageId) 86 | .findFirst() 87 | .orElseThrow(() -> new Fault(new OxalisAs4Exception("MessageID is missing from UserMessage"))); 88 | 89 | message.put(MessageId.MESSAGE_ID, new MessageId(messageId)); 90 | 91 | Optional.ofNullable(messaging) 92 | .map(Messaging::getUserMessage) 93 | .map(Collection::stream).orElseGet(Stream::empty) 94 | .map(UserMessage::getCollaborationInfo) 95 | .map(CollaborationInfo::getConversationId) 96 | .findFirst() 97 | .ifPresent(conversationId -> message.put("oxalis.as4.conversationId", conversationId)); 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4PayloadHeader.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import lombok.Getter; 4 | import lombok.ToString; 5 | import network.oxalis.vefa.peppol.common.model.*; 6 | 7 | import jakarta.xml.soap.MimeHeader; 8 | import java.util.Collection; 9 | import java.util.Date; 10 | import java.util.List; 11 | 12 | @Getter 13 | @ToString 14 | public class As4PayloadHeader extends Header { 15 | 16 | private final Header header; 17 | private final Collection mimeHeaders; 18 | private final String cid; 19 | 20 | private final String conversationId; 21 | 22 | public As4PayloadHeader(Header header, Collection mimeHeaders, String cid, String conversationId) { 23 | this.header = header; 24 | this.mimeHeaders = mimeHeaders; 25 | this.cid = cid; 26 | this.conversationId = conversationId; 27 | } 28 | 29 | @Override 30 | public Header sender(ParticipantIdentifier sender) { 31 | return header.sender(sender); 32 | } 33 | 34 | @Override 35 | public Header receiver(ParticipantIdentifier receiver) { 36 | return header.receiver(receiver); 37 | } 38 | 39 | @Override 40 | public Header process(ProcessIdentifier process) { 41 | return header.process(process); 42 | } 43 | 44 | @Override 45 | public Header documentType(DocumentTypeIdentifier documentType) { 46 | return header.documentType(documentType); 47 | } 48 | 49 | @Override 50 | public Header identifier(InstanceIdentifier identifier) { 51 | return header.identifier(identifier); 52 | } 53 | 54 | @Override 55 | public Header instanceType(InstanceType instanceType) { 56 | return header.instanceType(instanceType); 57 | } 58 | 59 | @Override 60 | public Header creationTimestamp(Date creationTimestamp) { 61 | return header.creationTimestamp(creationTimestamp); 62 | } 63 | 64 | @Override 65 | public Header c1CountryIdentifier(C1CountryIdentifier c1CountryIdentifier) { 66 | return header.c1CountryIdentifier(c1CountryIdentifier); 67 | } 68 | 69 | @Override 70 | public Header argument(ArgumentIdentifier identifier) { 71 | return header.argument(identifier); 72 | } 73 | 74 | @Override 75 | public Header arguments(List extras) { 76 | return header.arguments(extras); 77 | } 78 | 79 | @Override 80 | public ArgumentIdentifier getArgument(String key) { 81 | return header.getArgument(key); 82 | } 83 | 84 | @Override 85 | public List getArguments() { 86 | return header.getArguments(); 87 | } 88 | 89 | @Override 90 | public boolean equals(Object o) { 91 | return header.equals(o); 92 | } 93 | 94 | @Override 95 | public int hashCode() { 96 | return header.hashCode(); 97 | } 98 | 99 | @Override 100 | public ParticipantIdentifier getSender() { 101 | return header.getSender(); 102 | } 103 | 104 | @Override 105 | public ParticipantIdentifier getReceiver() { 106 | return header.getReceiver(); 107 | } 108 | 109 | @Override 110 | public ProcessIdentifier getProcess() { 111 | return header.getProcess(); 112 | } 113 | 114 | @Override 115 | public DocumentTypeIdentifier getDocumentType() { 116 | return header.getDocumentType(); 117 | } 118 | 119 | @Override 120 | public InstanceIdentifier getIdentifier() { 121 | return header.getIdentifier(); 122 | } 123 | 124 | @Override 125 | public InstanceType getInstanceType() { 126 | return header.getInstanceType(); 127 | } 128 | 129 | @Override 130 | public Date getCreationTimestamp() { 131 | return header.getCreationTimestamp(); 132 | } 133 | 134 | @Override 135 | public C1CountryIdentifier getC1CountryIdentifier() { 136 | return header.getC1CountryIdentifier(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4Provider.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import network.oxalis.as4.lang.OxalisAs4Exception; 6 | import org.apache.cxf.transport.http.AbstractHTTPDestination; 7 | 8 | import jakarta.annotation.Resource; 9 | import jakarta.servlet.http.HttpServletResponse; 10 | import jakarta.xml.soap.SOAPMessage; 11 | import jakarta.xml.ws.*; 12 | import jakarta.xml.ws.handler.MessageContext; 13 | import jakarta.xml.ws.soap.SOAPBinding; 14 | 15 | @WebServiceProvider 16 | @ServiceMode(value = Service.Mode.MESSAGE) 17 | @BindingType(value = SOAPBinding.SOAP12HTTP_BINDING) 18 | @Singleton 19 | public class As4Provider implements Provider { 20 | 21 | @Resource 22 | private WebServiceContext context; 23 | 24 | @Inject 25 | private As4InboundHandler handler; 26 | 27 | @Override 28 | public SOAPMessage invoke(SOAPMessage request) { 29 | MessageContext messageContext = context.getMessageContext(); 30 | HttpServletResponse httpRes = (HttpServletResponse) messageContext.get(AbstractHTTPDestination.HTTP_RESPONSE); 31 | httpRes.setStatus(HttpServletResponse.SC_OK); 32 | 33 | try { 34 | return handler.handle(request, messageContext); 35 | } catch (OxalisAs4Exception e) { 36 | httpRes.setStatus(HttpServletResponse.SC_BAD_REQUEST); 37 | throw new WebServiceException(e); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/As4Servlet.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.common.MerlinProvider; 7 | import network.oxalis.api.settings.Settings; 8 | import network.oxalis.commons.security.KeyStoreConf; 9 | import org.apache.cxf.BusFactory; 10 | import org.apache.cxf.ext.logging.LoggingFeature; 11 | import org.apache.cxf.jaxws.EndpointImpl; 12 | import org.apache.cxf.transport.servlet.CXFNonSpringServlet; 13 | import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JInInterceptor; 14 | import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor; 15 | import org.apache.wss4j.common.crypto.Merlin; 16 | 17 | import jakarta.servlet.ServletConfig; 18 | import jakarta.servlet.ServletException; 19 | import jakarta.servlet.http.HttpServletRequest; 20 | import jakarta.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | 23 | import static org.apache.cxf.rt.security.SecurityConstants.*; 24 | 25 | @Slf4j 26 | @Singleton 27 | public class As4Servlet extends CXFNonSpringServlet { 28 | 29 | @Inject 30 | private Settings settings; 31 | 32 | @Inject 33 | private As4EndpointsPublisher endpointsPublisher; 34 | 35 | @Inject 36 | private MerlinProvider merlinProvider; 37 | 38 | @Override 39 | protected void loadBus(ServletConfig servletConfig) { 40 | this.bus = BusFactory.getThreadDefaultBus(); 41 | 42 | EndpointImpl endpointImpl = endpointsPublisher.publish(getBus()); 43 | 44 | Merlin merlin = merlinProvider.getMerlin(); 45 | 46 | endpointImpl.getProperties().put(SIGNATURE_CRYPTO, merlin); 47 | endpointImpl.getProperties().put(SIGNATURE_PASSWORD, settings.getString(KeyStoreConf.KEY_PASSWORD)); 48 | endpointImpl.getProperties().put(SIGNATURE_USERNAME, settings.getString(KeyStoreConf.KEY_ALIAS)); 49 | 50 | endpointImpl.getProperties().put(ENCRYPT_CRYPTO, merlin); 51 | endpointImpl.getProperties().put(ENCRYPT_USERNAME, settings.getString(KeyStoreConf.KEY_ALIAS)); 52 | 53 | endpointImpl.getInInterceptors().add(new PolicyBasedWSS4JInInterceptor()); 54 | endpointImpl.getOutInterceptors().add(new PolicyBasedWSS4JOutInterceptor()); 55 | 56 | endpointImpl.getFeatures().add(new LoggingFeature()); 57 | } 58 | 59 | @Override 60 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException { 61 | try { 62 | response.setStatus(HttpServletResponse.SC_OK); 63 | response.getWriter().write("Hello AS4 world\n"); 64 | } catch (IOException e) { 65 | throw new ServletException("Unable to send response", e); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/AttachmentCleanupInterceptor.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import lombok.SneakyThrows; 4 | import org.apache.cxf.attachment.As4AttachmentDataSource; 5 | import org.apache.cxf.attachment.As4AttachmentDeserializer; 6 | import org.apache.cxf.interceptor.Fault; 7 | import org.apache.cxf.message.Attachment; 8 | import org.apache.cxf.message.Exchange; 9 | import org.apache.cxf.message.Message; 10 | import org.apache.cxf.phase.AbstractPhaseInterceptor; 11 | import org.apache.cxf.phase.Phase; 12 | 13 | import jakarta.activation.DataSource; 14 | 15 | public class AttachmentCleanupInterceptor extends AbstractPhaseInterceptor { 16 | 17 | public AttachmentCleanupInterceptor() { 18 | super(Phase.POST_INVOKE); 19 | } 20 | 21 | public void handleMessage(Message message) throws Fault { 22 | Exchange exchange = message.getExchange(); 23 | cleanRequestAttachment(exchange); 24 | } 25 | 26 | private void cleanRequestAttachment(Exchange exchange) { 27 | As4AttachmentDeserializer ad = exchange.getInMessage().get(As4AttachmentDeserializer.class); 28 | ad.getRemoved().forEach(this::close); 29 | } 30 | 31 | @SneakyThrows 32 | private void close(Attachment attachment) { 33 | DataSource dataSource = attachment.getDataHandler().getDataSource(); 34 | 35 | if (dataSource instanceof As4AttachmentDataSource) { 36 | As4AttachmentDataSource ads = (As4AttachmentDataSource) dataSource; 37 | ads.closeAll(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/MessagingHandler.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import javax.xml.namespace.QName; 4 | import jakarta.xml.ws.handler.MessageContext; 5 | import jakarta.xml.ws.handler.soap.SOAPHandler; 6 | import jakarta.xml.ws.handler.soap.SOAPMessageContext; 7 | import java.util.Collections; 8 | import java.util.Set; 9 | 10 | public class MessagingHandler implements SOAPHandler { 11 | @Override 12 | public Set getHeaders() { 13 | 14 | QName messagingHeader = new QName( 15 | "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/", 16 | "Messaging" 17 | ); 18 | 19 | return Collections.singleton(messagingHeader); 20 | } 21 | 22 | @Override 23 | public boolean handleMessage(SOAPMessageContext context) { 24 | return true; 25 | } 26 | 27 | @Override 28 | public boolean handleFault(SOAPMessageContext context) { 29 | return true; 30 | } 31 | 32 | @Override 33 | public void close(MessageContext context) { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/OxalisAS4Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2018 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.inbound; 24 | 25 | import network.oxalis.api.lang.OxalisLoadingException; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.util.Properties; 30 | 31 | /** 32 | * Provides access to selected Maven injected properties in the oxalis-as4-version.properties file. 33 | */ 34 | public class OxalisAS4Version { 35 | 36 | private static Properties properties; 37 | 38 | static { 39 | try (InputStream inputStream = OxalisAS4Version.class.getResourceAsStream("/oxalis-as4-version.properties")) { 40 | properties = new Properties(); 41 | properties.load(inputStream); 42 | } catch (IOException e) { 43 | throw new OxalisLoadingException(e.getMessage(), e); 44 | } 45 | } 46 | 47 | /** 48 | * The Oxalis version, taken from the POM 49 | */ 50 | public static String getVersion() { 51 | return properties.getProperty("oxalis.version"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/ProsessingContext.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import network.oxalis.api.timestamp.Timestamp; 6 | import org.w3.xmldsig.ReferenceType; 7 | 8 | import java.util.List; 9 | 10 | @Getter 11 | @AllArgsConstructor 12 | public class ProsessingContext { 13 | 14 | private Timestamp receiptTimestamp; 15 | private List referenceList; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/SetPolicyInInterceptor.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.util.PolicyService; 7 | import org.apache.cxf.phase.Phase; 8 | import org.apache.cxf.ws.policy.PolicyInInterceptor; 9 | 10 | @Slf4j 11 | @Singleton 12 | public class SetPolicyInInterceptor extends AbstractSetPolicyInterceptor { 13 | 14 | @Inject 15 | public SetPolicyInInterceptor(PolicyService policyService) { 16 | super(Phase.RECEIVE, policyService); 17 | addBefore(PolicyInInterceptor.class.getName()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/inbound/SetPolicyOutInterceptor.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.util.PolicyService; 7 | import org.apache.cxf.phase.Phase; 8 | import org.apache.cxf.ws.policy.PolicyOutInterceptor; 9 | 10 | @Slf4j 11 | @Singleton 12 | public class SetPolicyOutInterceptor extends AbstractSetPolicyInterceptor { 13 | 14 | @Inject 15 | public SetPolicyOutInterceptor(PolicyService policyService) { 16 | super(Phase.SETUP, policyService); 17 | addBefore(PolicyOutInterceptor.class.getName()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/lang/AS4Error.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.lang; 2 | 3 | import network.oxalis.as4.util.AS4ErrorCode; 4 | 5 | public interface AS4Error { 6 | 7 | AS4ErrorCode getErrorCode(); 8 | 9 | AS4ErrorCode.Severity getSeverity(); 10 | 11 | String getMessage(); 12 | 13 | Exception getException(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/lang/OxalisAs4Exception.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.lang; 2 | 3 | import network.oxalis.api.lang.OxalisException; 4 | import network.oxalis.as4.util.AS4ErrorCode; 5 | 6 | public class OxalisAs4Exception extends OxalisException implements AS4Error { 7 | 8 | private AS4ErrorCode errorCode = AS4ErrorCode.EBMS_0004; 9 | private AS4ErrorCode.Severity severity = AS4ErrorCode.Severity.ERROR; 10 | 11 | public OxalisAs4Exception(String message) { 12 | super(message); 13 | } 14 | 15 | public OxalisAs4Exception(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public OxalisAs4Exception(String message, AS4ErrorCode errorCode) { 20 | super(message); 21 | this.errorCode = errorCode; 22 | } 23 | 24 | public OxalisAs4Exception(String message, AS4ErrorCode errorCode, AS4ErrorCode.Severity severity) { 25 | super(message); 26 | this.errorCode = errorCode; 27 | this.severity = severity; 28 | } 29 | 30 | public OxalisAs4Exception(String message, Throwable cause, AS4ErrorCode errorCode) { 31 | super(message, cause); 32 | this.errorCode = errorCode; 33 | } 34 | 35 | public OxalisAs4Exception(String message, Throwable cause, AS4ErrorCode errorCode, AS4ErrorCode.Severity severity) { 36 | super(message, cause); 37 | this.errorCode = errorCode; 38 | this.severity = severity; 39 | } 40 | 41 | 42 | public AS4ErrorCode getErrorCode() { 43 | return errorCode; 44 | } 45 | 46 | public AS4ErrorCode.Severity getSeverity() { 47 | return severity; 48 | } 49 | 50 | @Override 51 | public Exception getException() { 52 | return this; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/lang/OxalisAs4TransmissionException.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.lang; 2 | 3 | import network.oxalis.api.lang.OxalisTransmissionException; 4 | import network.oxalis.as4.util.AS4ErrorCode; 5 | 6 | import java.net.URI; 7 | 8 | public class OxalisAs4TransmissionException extends OxalisTransmissionException implements AS4Error { 9 | 10 | private AS4ErrorCode errorCode = AS4ErrorCode.EBMS_0004; 11 | private AS4ErrorCode.Severity severity = AS4ErrorCode.Severity.ERROR; 12 | 13 | public OxalisAs4TransmissionException(String message) { 14 | super(message); 15 | } 16 | 17 | public OxalisAs4TransmissionException(String message, AS4ErrorCode errorCode, AS4ErrorCode.Severity severity) { 18 | super(message); 19 | this.errorCode = errorCode; 20 | this.severity = severity; 21 | } 22 | 23 | public OxalisAs4TransmissionException(String message, Throwable cause) { 24 | super(message, cause); 25 | } 26 | 27 | public OxalisAs4TransmissionException(URI url, Throwable cause) { 28 | super(url, cause); 29 | } 30 | 31 | public OxalisAs4TransmissionException(String msg, URI url, Throwable e) { 32 | super(msg, url, e); 33 | } 34 | 35 | @Override 36 | public AS4ErrorCode getErrorCode() { 37 | return errorCode; 38 | } 39 | 40 | @Override 41 | public AS4ErrorCode.Severity getSeverity() { 42 | return severity; 43 | } 44 | 45 | @Override 46 | public Exception getException() { 47 | return this; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/ActionProvider.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; 4 | 5 | public interface ActionProvider { 6 | 7 | String getAction(DocumentTypeIdentifier documentTypeIdentifier); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/As4MessageSenderFacade.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import com.google.inject.Inject; 4 | import network.oxalis.api.lang.OxalisTransmissionException; 5 | import network.oxalis.api.outbound.MessageSender; 6 | import network.oxalis.api.outbound.TransmissionRequest; 7 | import network.oxalis.api.outbound.TransmissionResponse; 8 | 9 | public class As4MessageSenderFacade implements MessageSender { 10 | 11 | private As4MessageSender messageSender; 12 | 13 | @Inject 14 | public As4MessageSenderFacade(As4MessageSender messageSender) { 15 | this.messageSender = messageSender; 16 | } 17 | 18 | @Override 19 | public TransmissionResponse send(TransmissionRequest transmissionRequest) throws OxalisTransmissionException { 20 | return messageSender.send(transmissionRequest); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/As4OutboundModule.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import com.google.inject.*; 4 | import com.google.inject.name.Names; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.common.AS4Constants; 7 | import network.oxalis.as4.config.As4Conf; 8 | import network.oxalis.as4.util.CompressionUtil; 9 | import network.oxalis.as4.util.PeppolConfiguration; 10 | import network.oxalis.api.outbound.MessageSender; 11 | import network.oxalis.api.settings.Settings; 12 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 13 | 14 | import java.security.Security; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | 18 | @Slf4j 19 | public class As4OutboundModule extends AbstractModule { 20 | 21 | @Override 22 | protected void configure() { 23 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 24 | Security.addProvider(new BouncyCastleProvider()); 25 | } 26 | 27 | bind(Key.get(MessageSender.class, Names.named("oxalis-as4"))) 28 | .to(As4MessageSenderFacade.class); 29 | 30 | bind(Key.get(ExecutorService.class, Names.named("compression-pool"))) 31 | .toProvider(() -> Executors.newFixedThreadPool(5)).in(Scopes.SINGLETON); 32 | 33 | bind(CompressionUtil.class); 34 | 35 | bind(MessagingProvider.class); 36 | 37 | bind(As4MessageSender.class); 38 | 39 | bind(TransmissionResponseConverter.class); 40 | } 41 | 42 | @Provides 43 | @Singleton 44 | public PeppolConfiguration getPeppolOutboundConfiguration(Settings settings) { 45 | String type = settings.getString(As4Conf.TYPE); 46 | 47 | if (AS4Constants.CEF_CONNECTIVITY.equalsIgnoreCase(type)) { 48 | return new PeppolConfiguration() { 49 | 50 | @Override 51 | public String getPartyIDType() { 52 | return "urn:oasis:names:tc:ebcore:partyid-type:unregistered"; 53 | } 54 | 55 | @Override 56 | public String getAgreementRef() { 57 | return null; 58 | } 59 | }; 60 | } 61 | 62 | return new PeppolConfiguration(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/As4TransmissionRequest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import network.oxalis.as4.common.As4MessageProperties; 4 | import network.oxalis.api.outbound.TransmissionRequest; 5 | 6 | import java.nio.charset.Charset; 7 | 8 | public interface As4TransmissionRequest extends TransmissionRequest { 9 | 10 | String getRefToMessageId(); 11 | 12 | String getMessageId(); 13 | 14 | String getConversationId(); 15 | 16 | As4MessageProperties getMessageProperties(); 17 | 18 | String getPayloadHref(); 19 | 20 | Charset getPayloadCharset(); 21 | 22 | String getCompressionType(); 23 | 24 | boolean isPing(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/As4TransmissionResponse.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import network.oxalis.api.model.TransmissionIdentifier; 4 | import network.oxalis.api.outbound.TransmissionRequest; 5 | import network.oxalis.api.outbound.TransmissionResponse; 6 | import network.oxalis.api.timestamp.Timestamp; 7 | import network.oxalis.as4.lang.OxalisAs4TransmissionException; 8 | import network.oxalis.vefa.peppol.common.model.*; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | public class As4TransmissionResponse implements TransmissionResponse { 16 | 17 | // Original transmission request is kept to allow easy access to immutable objects part of the request. 18 | private final TransmissionRequest transmissionRequest; 19 | private final TransmissionIdentifier transmissionIdentifier; 20 | private final Digest digest; 21 | private final Receipt receipt; 22 | private final List receipts; 23 | private final Date timestamp; 24 | private final OxalisAs4TransmissionException transmissionException; 25 | 26 | public As4TransmissionResponse(TransmissionIdentifier transmissionIdentifier, 27 | TransmissionRequest transmissionRequest, Digest digest, 28 | byte[] nativeEvidenceBytes, Timestamp timestamp, Date date) { 29 | this.transmissionIdentifier = transmissionIdentifier; 30 | this.transmissionRequest = transmissionRequest; 31 | this.digest = digest; 32 | this.receipt = Receipt.of("message/disposition-notification", nativeEvidenceBytes); 33 | this.timestamp = date; 34 | this.transmissionException = null; 35 | 36 | List receiptList = new ArrayList<>(); 37 | receiptList.add(receipt); 38 | timestamp.getReceipt().ifPresent(receiptList::add); 39 | 40 | this.receipts = Collections.unmodifiableList(receiptList); 41 | } 42 | 43 | public As4TransmissionResponse(TransmissionIdentifier transmissionIdentifier, TransmissionRequest transmissionRequest, OxalisAs4TransmissionException transmissionException) { 44 | this.transmissionRequest = transmissionRequest; 45 | this.transmissionIdentifier = transmissionIdentifier; 46 | this.transmissionException = transmissionException; 47 | this.digest = null; 48 | this.receipt = null; 49 | this.receipts = null; 50 | this.timestamp = null; 51 | } 52 | 53 | @Override 54 | public Header getHeader() { 55 | return transmissionRequest.getHeader(); 56 | } 57 | 58 | public TransmissionIdentifier getTransmissionIdentifier() { 59 | return transmissionIdentifier; 60 | } 61 | 62 | @Override 63 | public List getReceipts() { 64 | return receipts; 65 | } 66 | 67 | @Override 68 | public Endpoint getEndpoint() { 69 | return transmissionRequest.getEndpoint(); 70 | } 71 | 72 | @Override 73 | public Receipt primaryReceipt() { 74 | return receipt; 75 | } 76 | 77 | @Override 78 | public Digest getDigest() { 79 | return digest; 80 | } 81 | 82 | @Override 83 | public TransportProtocol getTransportProtocol() { 84 | return TransportProtocol.AS4; 85 | } 86 | 87 | @Override 88 | public Date getTimestamp() { 89 | return timestamp; 90 | } 91 | 92 | public OxalisAs4TransmissionException getTransmissionException() { 93 | return transmissionException; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/BrowserTypeProvider.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import lombok.extern.slf4j.Slf4j; 6 | import network.oxalis.as4.inbound.OxalisAS4Version; 7 | import network.oxalis.commons.util.OxalisVersion; 8 | import org.bouncycastle.asn1.x500.RDN; 9 | import org.bouncycastle.asn1.x500.X500Name; 10 | import org.bouncycastle.asn1.x500.style.BCStyle; 11 | import org.bouncycastle.asn1.x500.style.IETFUtils; 12 | import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; 13 | 14 | import java.security.cert.CertificateEncodingException; 15 | import java.security.cert.X509Certificate; 16 | 17 | @Slf4j 18 | @Singleton 19 | public class BrowserTypeProvider { 20 | 21 | private final X509Certificate certificate; 22 | 23 | @Inject 24 | public BrowserTypeProvider(X509Certificate certificate) { 25 | this.certificate = certificate; 26 | } 27 | 28 | public String getBrowserType() { 29 | return String.format("Oxalis %s / AS4 %s / %s", 30 | OxalisVersion.getVersion(), 31 | OxalisAS4Version.getVersion(), 32 | getCN()); 33 | } 34 | 35 | private String getCN() { 36 | try { 37 | X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); 38 | RDN cn = x500name.getRDNs(BCStyle.CN)[0]; 39 | return IETFUtils.valueToString(cn.getFirst().getValue()); 40 | } catch (CertificateEncodingException e) { 41 | log.warn("Could not extract CN from certificate", e); 42 | return "Unknown"; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/DefaultActionProvider.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import network.oxalis.as4.util.TransmissionRequestUtil; 4 | import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; 5 | 6 | public class DefaultActionProvider implements ActionProvider { 7 | @Override 8 | public String getAction(DocumentTypeIdentifier documentTypeIdentifier) { 9 | return TransmissionRequestUtil.translateDocumentTypeToAction(documentTypeIdentifier); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/LoggingBeforeSecurityInInterceptor.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import org.apache.cxf.common.injection.NoJSR250Annotations; 4 | import org.apache.cxf.common.util.StringUtils; 5 | import org.apache.cxf.ext.logging.AbstractLoggingInterceptor; 6 | import org.apache.cxf.ext.logging.event.LogEvent; 7 | import org.apache.cxf.ext.logging.event.LogEventSender; 8 | import org.apache.cxf.ext.logging.event.PrintWriterEventSender; 9 | import org.apache.cxf.ext.logging.slf4j.Slf4jVerboseEventSender; 10 | import org.apache.cxf.interceptor.Fault; 11 | import org.apache.cxf.io.CachedOutputStream; 12 | import org.apache.cxf.io.CachedWriter; 13 | import org.apache.cxf.message.Message; 14 | import org.apache.cxf.phase.Phase; 15 | 16 | import java.io.IOException; 17 | import java.io.PrintWriter; 18 | import java.nio.charset.StandardCharsets; 19 | 20 | @NoJSR250Annotations 21 | public class LoggingBeforeSecurityInInterceptor extends AbstractLoggingInterceptor { 22 | 23 | public LoggingBeforeSecurityInInterceptor() { 24 | this(new Slf4jVerboseEventSender()); 25 | } 26 | 27 | public LoggingBeforeSecurityInInterceptor(PrintWriter writer) { 28 | this(new PrintWriterEventSender(writer)); 29 | } 30 | 31 | public LoggingBeforeSecurityInInterceptor(LogEventSender sender) { 32 | super(Phase.RECEIVE, sender); 33 | } 34 | 35 | public void handleMessage(Message message) throws Fault { 36 | if (isLoggingDisabledNow(message)) { 37 | return; 38 | } 39 | createExchangeId(message); 40 | final LogEvent event = eventMapper.map(message); 41 | if (shouldLogContent(event)) { 42 | addContent(message, event); 43 | } else { 44 | event.setPayload(AbstractLoggingInterceptor.CONTENT_SUPPRESSED); 45 | } 46 | sender.send(event); 47 | } 48 | 49 | private void addContent(Message message, final LogEvent event) { 50 | try { 51 | CachedOutputStream cos = message.getContent(CachedOutputStream.class); 52 | if (cos != null) { 53 | handleOutputStream(event, message, cos); 54 | } else { 55 | CachedWriter writer = message.getContent(CachedWriter.class); 56 | if (writer != null) { 57 | handleWriter(event, writer); 58 | } 59 | } 60 | } catch (IOException e) { 61 | throw new Fault(e); 62 | } 63 | } 64 | 65 | private void handleOutputStream(final LogEvent event, Message message, CachedOutputStream cos) throws IOException { 66 | String encoding = (String) message.get(Message.ENCODING); 67 | if (StringUtils.isEmpty(encoding)) { 68 | encoding = StandardCharsets.UTF_8.name(); 69 | } 70 | StringBuilder payload = new StringBuilder(); 71 | cos.writeCacheTo(payload, encoding, limit); 72 | cos.close(); 73 | event.setPayload(payload.toString()); 74 | boolean isTruncated = cos.size() > limit && limit != -1; 75 | event.setTruncated(isTruncated); 76 | event.setFullContentFile(cos.getTempFile()); 77 | } 78 | 79 | private void handleWriter(final LogEvent event, CachedWriter writer) throws IOException { 80 | boolean isTruncated = writer.size() > limit && limit != -1; 81 | StringBuilder payload = new StringBuilder(); 82 | writer.writeCacheTo(payload, limit); 83 | writer.close(); 84 | event.setPayload(payload.toString()); 85 | event.setTruncated(isTruncated); 86 | event.setFullContentFile(writer.getTempFile()); 87 | } 88 | 89 | int getWireTapLimit() { 90 | if (limit == -1) { 91 | return -1; 92 | } else if (limit == Integer.MAX_VALUE) { 93 | return limit; 94 | } else { 95 | // add limit +1 as limit for the wiretab in order to read one byte more, so that truncated 96 | // is correctly calculated in LogginInIntecepteor! 97 | // See code line : boolean isTruncated = cos.size() > limit && limit != -1; 98 | // cos is here the outputstream read by the wiretab which will return for cos.size() the 99 | // limit in the truncated case! 100 | return limit + 1; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/outbound/TransmissionResponseConverter.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import com.google.inject.Inject; 4 | import network.oxalis.api.lang.TimestampException; 5 | import network.oxalis.api.model.Direction; 6 | import network.oxalis.api.model.TransmissionIdentifier; 7 | import network.oxalis.api.outbound.TransmissionRequest; 8 | import network.oxalis.api.outbound.TransmissionResponse; 9 | import network.oxalis.api.timestamp.Timestamp; 10 | import network.oxalis.api.timestamp.TimestampProvider; 11 | import network.oxalis.as4.lang.OxalisAs4TransmissionException; 12 | import network.oxalis.as4.util.AS4ErrorCode; 13 | import network.oxalis.as4.util.Marshalling; 14 | import network.oxalis.commons.bouncycastle.BCHelper; 15 | import network.oxalis.vefa.peppol.common.code.DigestMethod; 16 | import network.oxalis.vefa.peppol.common.model.Digest; 17 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Error; 18 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.SignalMessage; 19 | import org.w3c.dom.Node; 20 | import org.w3c.dom.NodeList; 21 | 22 | import jakarta.xml.bind.JAXBContext; 23 | import jakarta.xml.bind.JAXBException; 24 | import jakarta.xml.bind.Unmarshaller; 25 | import jakarta.xml.soap.SOAPException; 26 | import jakarta.xml.soap.SOAPMessage; 27 | import java.io.ByteArrayOutputStream; 28 | import java.io.IOException; 29 | import java.security.MessageDigest; 30 | import java.security.NoSuchAlgorithmException; 31 | 32 | import static network.oxalis.as4.util.Constants.DIGEST_ALGORITHM_SHA256; 33 | 34 | public class TransmissionResponseConverter { 35 | 36 | private final JAXBContext jaxbContext = Marshalling.getInstance(); 37 | private final TimestampProvider timestampProvider; 38 | 39 | @Inject 40 | public TransmissionResponseConverter(TimestampProvider timestampProvider) { 41 | this.timestampProvider = timestampProvider; 42 | } 43 | 44 | public TransmissionResponse convert(TransmissionRequest request, SOAPMessage response) throws OxalisAs4TransmissionException { 45 | SignalMessage signalMessage = getSignalMessage(response); 46 | 47 | String refToMessageId = signalMessage.getMessageInfo().getRefToMessageId(); 48 | TransmissionIdentifier ti = TransmissionIdentifier.of(refToMessageId); 49 | 50 | if (!signalMessage.getError().isEmpty()) { 51 | Error error = signalMessage.getError().get(0); 52 | 53 | throw new OxalisAs4TransmissionException( 54 | error.getErrorDetail(), 55 | AS4ErrorCode.nameOf(error.getErrorCode()), 56 | AS4ErrorCode.Severity.nameOf(error.getSeverity())); 57 | } 58 | 59 | Timestamp ts = getTimestamp(); 60 | Digest digest = getDigest(); 61 | 62 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 63 | try { 64 | response.writeTo(bos); 65 | } catch (SOAPException | IOException e) { 66 | throw new OxalisAs4TransmissionException("Could not write response", e); 67 | } 68 | 69 | return new As4TransmissionResponse( 70 | ti, 71 | request, 72 | digest, 73 | bos.toByteArray(), 74 | ts, 75 | ts.getDate() 76 | ); 77 | } 78 | 79 | private Digest getDigest() throws OxalisAs4TransmissionException { 80 | try { 81 | MessageDigest md = BCHelper.getMessageDigest(DIGEST_ALGORITHM_SHA256); 82 | return Digest.of(DigestMethod.SHA256, md.digest()); 83 | } catch (NoSuchAlgorithmException e) { 84 | throw new OxalisAs4TransmissionException("Could not create message digest", e); 85 | } 86 | } 87 | 88 | private Timestamp getTimestamp() throws OxalisAs4TransmissionException { 89 | try { 90 | return timestampProvider.generate(null, Direction.OUT); 91 | } catch (TimestampException e) { 92 | throw new OxalisAs4TransmissionException("Could not create timestamp", e); 93 | } 94 | } 95 | 96 | private SignalMessage getSignalMessage(SOAPMessage soapMessage) throws OxalisAs4TransmissionException { 97 | Node signalNode = getSignalNode(soapMessage); 98 | 99 | try { 100 | Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 101 | return unmarshaller.unmarshal(signalNode, SignalMessage.class).getValue(); 102 | } catch (JAXBException e) { 103 | throw new OxalisAs4TransmissionException("Could not create unmarshaller", e); 104 | } 105 | } 106 | 107 | private Node getSignalNode(SOAPMessage soapMessage) throws OxalisAs4TransmissionException { 108 | try { 109 | NodeList signalNodeList = soapMessage.getSOAPHeader().getElementsByTagNameNS("*", "SignalMessage"); 110 | if (signalNodeList.getLength() != 1) { 111 | throw new OxalisAs4TransmissionException("SOAP header contains zero or multiple SignalMessage elements, should only contain one"); 112 | } 113 | return signalNodeList.item(0); 114 | } catch (SOAPException e) { 115 | throw new OxalisAs4TransmissionException("Could not access response body", e); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/AS4ErrorCode.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import static network.oxalis.as4.util.AS4ErrorCode.Category.*; 4 | import static network.oxalis.as4.util.AS4ErrorCode.Origin.EBMS; 5 | import static network.oxalis.as4.util.AS4ErrorCode.Origin.SECURITY; 6 | 7 | public enum AS4ErrorCode { 8 | 9 | EBMS_0001("EBMS:0001", "ValueNotRecognized", CONTENT, EBMS), 10 | EBMS_0002("EBMS:0002", "FeatureNotSupported", CONTENT, EBMS), 11 | EBMS_0003("EBMS:0003", "ValueInconsistent", CONTENT, EBMS), 12 | EBMS_0004("EBMS:0004", "Other", CONTENT, EBMS), 13 | EBMS_0005("EBMS:0005", "ConnectionFailure", COMMUNICATION, EBMS), 14 | EBMS_0006("EBMS:0006", "EmptyMessagePartitionFlow", COMMUNICATION, EBMS), 15 | EBMS_0007("EBMS:0007", "MimeInconsistency", UNPACKAGING, EBMS), 16 | EBMS_0008("EBMS:0008", "FeatureNotSupported", UNPACKAGING, EBMS), 17 | EBMS_0009("EBMS:0009", "InvalidHeader", UNPACKAGING, EBMS), 18 | EBMS_0010("EBMS:0010", "ProcessingModeMismatch", PROCESSING, EBMS), 19 | 20 | EBMS_0101("EBMS:0101", "FailedAuthentication", PROCESSING, SECURITY), 21 | EBMS_0102("EBMS:0102", "FailedDecryption", PROCESSING, SECURITY), 22 | EBMS_0103("EBMS:0103", "PolicyNoncompliance", PROCESSING, SECURITY), 23 | EBMS_0201("EBMS:0201", "DysfunctionalReliability", PROCESSING, SECURITY), 24 | EBMS_0202("EBMS:0202", "DeliveryFailure", COMMUNICATION, SECURITY), 25 | 26 | EBMS_0301("EBMS:0301", "MissingReceipt", COMMUNICATION, EBMS), 27 | EBMS_0303("EBMS:0303", "DecompressionFailure", COMMUNICATION, EBMS); 28 | 29 | private String errorCode; 30 | private String shortDescription; 31 | private Category catgory; 32 | private Origin origin; 33 | 34 | AS4ErrorCode(String errorCode, String shortDescription, Category category, Origin origin){ 35 | this.errorCode = errorCode; 36 | this.shortDescription = shortDescription; 37 | this.catgory = category; 38 | this.origin = origin; 39 | } 40 | 41 | public String getErrorCode() { 42 | return errorCode; 43 | } 44 | 45 | public String getShortDescription() { 46 | return shortDescription; 47 | } 48 | 49 | public Category getCatgory() { 50 | return catgory; 51 | } 52 | 53 | public Origin getOrigin() { 54 | return origin; 55 | } 56 | 57 | public static AS4ErrorCode nameOf(String name){ 58 | for(AS4ErrorCode errorCode : AS4ErrorCode.values()){ 59 | if (errorCode.toString().equalsIgnoreCase(name)){ 60 | return errorCode; 61 | } 62 | } 63 | 64 | return null; 65 | } 66 | 67 | 68 | @Override 69 | public String toString() { 70 | return getErrorCode(); 71 | } 72 | 73 | public enum Category{ 74 | CONTENT, 75 | COMMUNICATION, 76 | UNPACKAGING, 77 | PROCESSING; 78 | 79 | @Override 80 | public String toString() { 81 | return name().substring(0,1).toUpperCase() + name().substring(1).toLowerCase(); 82 | } 83 | } 84 | 85 | public enum Origin{ 86 | EBMS, 87 | SECURITY, 88 | RELIABILITY; 89 | 90 | @Override 91 | public String toString() { 92 | return name().toLowerCase(); 93 | } 94 | } 95 | 96 | public enum Severity{ 97 | ERROR, 98 | FAILURE, 99 | WARNING; 100 | 101 | @Override 102 | public String toString() { 103 | return name().toLowerCase(); 104 | } 105 | 106 | public static Severity nameOf(String name){ 107 | for(Severity severity : Severity.values()){ 108 | if (severity.toString().equalsIgnoreCase(name)){ 109 | return severity; 110 | } 111 | } 112 | 113 | return null; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/As4MessageFactory.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import network.oxalis.as4.api.MessageIdGenerator; 6 | import network.oxalis.as4.lang.AS4Error; 7 | import network.oxalis.as4.lang.OxalisAs4Exception; 8 | import network.oxalis.as4.inbound.ProsessingContext; 9 | import org.apache.cxf.interceptor.Fault; 10 | import org.oasis_open.docs.ebxml_bp.ebbp_signals_2.MessagePartNRInformation; 11 | import org.oasis_open.docs.ebxml_bp.ebbp_signals_2.NonRepudiationInformation; 12 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Error; 13 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.*; 14 | 15 | import jakarta.xml.bind.JAXBContext; 16 | import jakarta.xml.bind.JAXBElement; 17 | import javax.xml.datatype.XMLGregorianCalendar; 18 | import jakarta.xml.soap.*; 19 | import java.util.Date; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | @Singleton 24 | public class As4MessageFactory { 25 | 26 | private final MessageIdGenerator messageIdGenerator; 27 | private final MessageFactory messageFactory; 28 | private final JAXBContext jaxbContext; 29 | 30 | @Inject 31 | public As4MessageFactory(MessageIdGenerator messageIdGenerator) throws SOAPException { 32 | this( 33 | messageIdGenerator, 34 | MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL), 35 | Marshalling.getInstance() 36 | ); 37 | } 38 | 39 | public As4MessageFactory(MessageIdGenerator messageIdGenerator, MessageFactory messageFactory, JAXBContext jaxbContext) { 40 | this.messageFactory = messageFactory; 41 | this.jaxbContext = jaxbContext; 42 | this.messageIdGenerator = messageIdGenerator; 43 | } 44 | 45 | public SOAPMessage createReceiptMessage(UserMessage inUserMessage, ProsessingContext prosessingContext) throws OxalisAs4Exception { 46 | 47 | XMLGregorianCalendar xmlGc = XMLUtil.dateToXMLGeorgianCalendar( 48 | prosessingContext.getReceiptTimestamp().getDate() 49 | ); 50 | 51 | MessageInfo messageInfo = new MessageInfo(); 52 | messageInfo.setTimestamp(xmlGc); 53 | messageInfo.setMessageId(messageIdGenerator.generate()); 54 | messageInfo.setRefToMessageId(inUserMessage.getMessageInfo().getMessageId()); 55 | 56 | List mpList = prosessingContext.getReferenceList().stream() 57 | .map(reference -> { 58 | MessagePartNRInformation messagePartNRInformation = new MessagePartNRInformation(); 59 | messagePartNRInformation.setReference(reference); 60 | return messagePartNRInformation; 61 | }) 62 | .collect(Collectors.toList()); 63 | 64 | NonRepudiationInformation nri = new NonRepudiationInformation(); 65 | nri.getMessagePartNRInformation().addAll(mpList); 66 | 67 | Receipt receipt = new Receipt(); 68 | receipt.getAny().add(nri); 69 | 70 | SignalMessage signalMessage = new SignalMessage(); 71 | signalMessage.setMessageInfo(messageInfo); 72 | signalMessage.setReceipt(receipt); 73 | 74 | return marshalSignalMessage(signalMessage); 75 | } 76 | 77 | 78 | public SOAPMessage createErrorMessage(String messageId, AS4Error as4Error) { 79 | try { 80 | 81 | XMLGregorianCalendar currentDate = XMLUtil.dateToXMLGeorgianCalendar(new Date()); 82 | 83 | 84 | MessageInfo messageInfo = new MessageInfo(); 85 | messageInfo.setRefToMessageId(messageId); 86 | messageInfo.setTimestamp(currentDate); 87 | messageInfo.setMessageId(messageIdGenerator.generate()); 88 | 89 | Error error = new Error(); 90 | error.setRefToMessageInError(messageId); 91 | error.setErrorCode(as4Error.getErrorCode().toString()); 92 | error.setErrorDetail(getErrorDetail(as4Error)); 93 | error.setShortDescription(as4Error.getErrorCode().getShortDescription()); 94 | error.setOrigin(as4Error.getErrorCode().getOrigin().toString()); 95 | error.setCategory(as4Error.getErrorCode().getCatgory().toString()); 96 | error.setSeverity(as4Error.getSeverity().toString()); 97 | 98 | SignalMessage signalMessage = new SignalMessage(); 99 | signalMessage.setMessageInfo(messageInfo); 100 | signalMessage.getError().add(error); 101 | 102 | return marshalSignalMessage(signalMessage); 103 | 104 | } catch (OxalisAs4Exception e) { 105 | throw new Fault(e.getCause()); 106 | } 107 | } 108 | 109 | private String getErrorDetail(AS4Error as4Error) { 110 | StringBuilder sb = new StringBuilder(); 111 | 112 | sb.append(as4Error.getMessage()); 113 | 114 | Throwable throwable = as4Error.getException(); 115 | 116 | while (throwable.getCause() != null) { 117 | throwable = throwable.getCause(); 118 | sb.append("\ncause: ").append(throwable.getMessage()); 119 | } 120 | 121 | return sb.toString(); 122 | } 123 | 124 | public SOAPMessage marshalSignalMessage(SignalMessage signalMessage) throws OxalisAs4Exception { 125 | try { 126 | SOAPMessage message = messageFactory.createMessage(); 127 | SOAPHeader soapHeader = message.getSOAPHeader(); 128 | 129 | SOAPHeaderElement messagingHeader = soapHeader.addHeaderElement(Constants.MESSAGING_QNAME); 130 | messagingHeader.setMustUnderstand(true); 131 | 132 | JAXBElement userMessageJAXBElement = new JAXBElement<>( 133 | Constants.SIGNAL_MESSAGE_QNAME, 134 | SignalMessage.class, 135 | signalMessage 136 | ); 137 | 138 | jaxbContext.createMarshaller().marshal(userMessageJAXBElement, messagingHeader); 139 | 140 | return message; 141 | } catch (Exception e) { 142 | throw new OxalisAs4Exception("Unable to marshal SignalMessage", e, AS4ErrorCode.EBMS_0004); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/CompressionUtil.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import org.apache.cxf.helpers.IOUtils; 4 | import org.apache.cxf.io.CacheSizeExceededException; 5 | import org.apache.cxf.io.CachedOutputStream; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.zip.GZIPOutputStream; 10 | 11 | public class CompressionUtil { 12 | 13 | /** 14 | * Gets Compressed Stream for given input Stream 15 | * 16 | * @param sourceStream : Input Stream to be compressed to 17 | * @return Compressed Stream 18 | * @throws IOException when some thing bad happens 19 | */ 20 | public InputStream getCompressedStream(final InputStream sourceStream) throws IOException { 21 | 22 | if (sourceStream == null) { 23 | throw new IllegalArgumentException("Source Stream cannot be NULL"); 24 | } 25 | 26 | CachedOutputStream cache = new CachedOutputStream(); 27 | 28 | try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(cache)) { 29 | IOUtils.copyAndCloseInput(sourceStream, gzipOutputStream); 30 | gzipOutputStream.flush(); 31 | gzipOutputStream.finish(); 32 | return cache.getInputStream(); 33 | } catch (CacheSizeExceededException | IOException cee) { 34 | sourceStream.close(); 35 | throw cee; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/Constants.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | import javax.xml.namespace.QName; 6 | 7 | @UtilityClass 8 | public class Constants { 9 | 10 | public static final String EBMS_NAMESPACE = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/"; 11 | 12 | public static final QName MESSAGING_QNAME = new QName(EBMS_NAMESPACE, "Messaging", "eb"); 13 | public static final QName USER_MESSAGE_QNAME = new QName(EBMS_NAMESPACE, "UserMessage"); 14 | public static final QName SIGNAL_MESSAGE_QNAME = new QName(EBMS_NAMESPACE, "SignalMessage"); 15 | 16 | public static final String DIGEST_ALGORITHM_SHA256 = "sha256"; 17 | 18 | public static final String TEST_SERVICE = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/service"; 19 | public static final String TEST_ACTION = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/test"; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/GeneralUtils.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | import java.util.Iterator; 6 | import java.util.Spliterators; 7 | import java.util.stream.Stream; 8 | import java.util.stream.StreamSupport; 9 | 10 | @UtilityClass 11 | public class GeneralUtils { 12 | 13 | public static Stream iteratorToStreamOfUnknownSize(Iterator iterator, int characteristics, boolean parallel) { 14 | return StreamSupport.stream( 15 | Spliterators.spliteratorUnknownSize(iterator, characteristics), 16 | parallel); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/InputStreamDataSource.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import jakarta.activation.DataSource; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | public class InputStreamDataSource implements DataSource { 9 | 10 | private final InputStream inputStream; 11 | 12 | private final String contentType; 13 | 14 | public InputStreamDataSource(InputStream inputStream, String contentType) { 15 | this.inputStream = inputStream; 16 | this.contentType = contentType; 17 | } 18 | 19 | @Override 20 | public InputStream getInputStream () throws IOException { 21 | return inputStream; 22 | } 23 | 24 | @Override 25 | public OutputStream getOutputStream () { 26 | throw new UnsupportedOperationException("Read-only"); 27 | } 28 | 29 | @Override 30 | public String getContentType () { 31 | return contentType; 32 | } 33 | 34 | @Override 35 | public String getName () { 36 | throw new UnsupportedOperationException("DataSource name not available"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/Marshalling.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import network.oxalis.peppol.sbdh.jaxb.StandardBusinessDocument; 5 | import org.oasis_open.docs.ebxml_bp.ebbp_signals_2.NonRepudiationInformation; 6 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Messaging; 7 | import org.w3.xmldsig.ReferenceType; 8 | import org.xmlsoap.schemas.soap.envelope.Envelope; 9 | 10 | import jakarta.xml.bind.JAXBContext; 11 | import jakarta.xml.bind.JAXBException; 12 | 13 | @UtilityClass 14 | public class Marshalling { 15 | 16 | public static JAXBContext getInstance() { 17 | return InitializedMarshaller.instance; 18 | } 19 | 20 | public static JAXBContext createMarshaller() { 21 | try { 22 | return JAXBContext.newInstance( 23 | StandardBusinessDocument.class, 24 | Envelope.class, 25 | org.w3.soap.Envelope.class, 26 | ReferenceType.class, 27 | NonRepudiationInformation.class, 28 | Messaging.class 29 | ); 30 | } catch (JAXBException e) { 31 | throw new RuntimeException("Unable to create marshaller for AS4 documents", e); 32 | } 33 | } 34 | 35 | private static class InitializedMarshaller { 36 | private static final JAXBContext instance = createMarshaller(); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/MessageId.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @AllArgsConstructor 8 | public class MessageId { 9 | public static final String MESSAGE_ID = "oxalis.as4.messageId"; 10 | 11 | private String value; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/MessageIdUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.util; 24 | 25 | import lombok.experimental.UtilityClass; 26 | 27 | import java.util.regex.Pattern; 28 | 29 | @UtilityClass 30 | public class MessageIdUtil { 31 | 32 | private static final String ATEXT = "[A-Za-z0-9!#\\$%&'\\*\\+\\-/=\\?\\^_`\\{}\\|~]+"; 33 | 34 | private static final Pattern PATTERN = 35 | Pattern.compile("^" + ATEXT + "(\\." + ATEXT + ")*@" + ATEXT + "(\\." + ATEXT + ")*$"); 36 | 37 | public static boolean verify(String identifier) { 38 | return PATTERN.matcher(identifier).matches(); 39 | } 40 | 41 | public static String wrap(String cid) { 42 | if (cid == null) { 43 | return null; 44 | } 45 | 46 | if (cid.startsWith("<") && cid.endsWith(">")) { 47 | return cid; 48 | } 49 | 50 | return String.format("<%s>", cid); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/PeppolConfiguration.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.Getter; 4 | import network.oxalis.api.tag.Tag; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static org.apache.wss4j.dom.handler.WSHandlerConstants.ENCRYPT; 10 | import static org.apache.wss4j.dom.handler.WSHandlerConstants.SIGNATURE; 11 | 12 | @Getter 13 | public class PeppolConfiguration implements Tag { 14 | public static final String IDENTIFIER = "AS4.OUTBOUND.PEPPOL"; 15 | 16 | private List actions = Arrays.asList(SIGNATURE, ENCRYPT); 17 | 18 | private String partyIDType = "urn:fdc:peppol.eu:2017:identifiers:ap"; 19 | private String agreementRef = "urn:fdc:peppol.eu:2017:agreements:tia:ap_provider"; 20 | 21 | private String fromRole = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/initiator"; 22 | private String toRole = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/responder"; 23 | 24 | 25 | @Override 26 | public String getIdentifier() { 27 | return IDENTIFIER; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/PolicyService.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import network.oxalis.as4.lang.OxalisAs4TransmissionException; 5 | import network.oxalis.api.outbound.TransmissionRequest; 6 | import network.oxalis.as4.outbound.ActionProvider; 7 | import network.oxalis.vefa.peppol.common.model.ProcessIdentifier; 8 | import org.apache.cxf.Bus; 9 | import org.apache.cxf.BusFactory; 10 | import org.apache.cxf.ws.policy.PolicyBuilder; 11 | import org.apache.neethi.Policy; 12 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.CollaborationInfo; 13 | import org.xml.sax.SAXException; 14 | 15 | import javax.xml.parsers.ParserConfigurationException; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | 19 | @Slf4j 20 | public class PolicyService { 21 | 22 | private final ActionProvider actionProvider; 23 | 24 | public PolicyService(ActionProvider actionProvider) { 25 | this.actionProvider = actionProvider; 26 | } 27 | 28 | public Policy getPolicy() throws OxalisAs4TransmissionException { 29 | Bus bus = BusFactory.getThreadDefaultBus(); 30 | return getPolicy(bus); 31 | } 32 | 33 | public Policy getPolicy(Bus bus) throws OxalisAs4TransmissionException { 34 | return getPolicy(getDefaultPolicy(), bus); 35 | } 36 | 37 | public Policy getPolicy(TransmissionRequest request) throws OxalisAs4TransmissionException { 38 | Bus bus = BusFactory.getThreadDefaultBus(); 39 | return getPolicy(request, bus); 40 | } 41 | 42 | public Policy getPolicy(TransmissionRequest request, Bus bus) throws OxalisAs4TransmissionException { 43 | String action = actionProvider.getAction(request.getHeader().getDocumentType()); 44 | ProcessIdentifier process = request.getHeader().getProcess(); 45 | String service = process.getIdentifier(); 46 | return getPolicy(getPolicyClasspath(action, service), bus); 47 | } 48 | 49 | public Policy getPolicy(CollaborationInfo collaborationInfo) throws OxalisAs4TransmissionException { 50 | Bus bus = BusFactory.getThreadDefaultBus(); 51 | return getPolicy(collaborationInfo, bus); 52 | } 53 | 54 | public Policy getPolicy(CollaborationInfo collaborationInfo, Bus bus) throws OxalisAs4TransmissionException { 55 | return getPolicy(getPolicyClasspath(collaborationInfo.getAction(), collaborationInfo.getService().getValue()), bus); 56 | } 57 | 58 | private Policy getPolicy(String policyClasspath, Bus bus) throws OxalisAs4TransmissionException { 59 | try { 60 | log.debug("Policy classpath: {}", policyClasspath); 61 | InputStream policyStream = getClass().getResourceAsStream(policyClasspath); 62 | PolicyBuilder builder = bus.getExtension(PolicyBuilder.class); 63 | return builder.getPolicy(policyStream); 64 | } catch (SAXException | ParserConfigurationException | IOException e) { 65 | throw new OxalisAs4TransmissionException("Failed to get WS Policy", e); 66 | } 67 | } 68 | 69 | protected String getPolicyClasspath(String action, String service) { 70 | return getDefaultPolicy(); 71 | } 72 | 73 | protected String getDefaultPolicy() { 74 | return "/eDeliveryAS4Policy_BST.xml"; 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/TransmissionRequestUtil.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import network.oxalis.as4.common.As4MessageProperty; 5 | import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; 6 | import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; 7 | 8 | @UtilityClass 9 | public class TransmissionRequestUtil { 10 | 11 | public static String translateDocumentTypeToAction(DocumentTypeIdentifier documentTypeIdentifier) { 12 | return documentTypeIdentifier.getScheme() == null || 13 | documentTypeIdentifier.getScheme().getIdentifier() == null || 14 | documentTypeIdentifier.getScheme().getIdentifier().trim().isEmpty() ? 15 | 16 | documentTypeIdentifier.getIdentifier() : 17 | documentTypeIdentifier.toString(); 18 | } 19 | 20 | public static As4MessageProperty toAs4MessageProperty(String name, ParticipantIdentifier documentTypeIdentifier) { 21 | return documentTypeIdentifier.getScheme() == null || 22 | documentTypeIdentifier.getScheme().getIdentifier() == null || 23 | documentTypeIdentifier.getScheme().getIdentifier().trim().isEmpty() 24 | ? new As4MessageProperty(name, documentTypeIdentifier.getIdentifier()) 25 | : new As4MessageProperty(name, documentTypeIdentifier.getScheme().getIdentifier(), documentTypeIdentifier.getIdentifier()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/network/oxalis/as4/util/XMLUtil.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import network.oxalis.as4.lang.OxalisAs4Exception; 5 | 6 | import javax.xml.datatype.DatatypeConfigurationException; 7 | import javax.xml.datatype.DatatypeFactory; 8 | import javax.xml.datatype.XMLGregorianCalendar; 9 | import java.util.Date; 10 | import java.util.GregorianCalendar; 11 | 12 | @UtilityClass 13 | public class XMLUtil { 14 | 15 | public static XMLGregorianCalendar dateToXMLGeorgianCalendar(Date date) throws OxalisAs4Exception { 16 | try { 17 | GregorianCalendar gc = new GregorianCalendar(); 18 | gc.setTime(date); 19 | 20 | return DatatypeFactory.newInstance().newXMLGregorianCalendar(gc); 21 | } catch (DatatypeConfigurationException e) { 22 | throw new OxalisAs4Exception("Unable to convert timestamp to XML", e); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/apache/cxf/attachment/As4AttachmentDataSource.java: -------------------------------------------------------------------------------- 1 | package org.apache.cxf.attachment; 2 | 3 | import lombok.Getter; 4 | import lombok.SneakyThrows; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Getter 12 | public class As4AttachmentDataSource extends AttachmentDataSource { 13 | 14 | List inputStreams = new ArrayList<>(); 15 | 16 | public As4AttachmentDataSource(String ctParam, InputStream inParam) throws IOException { 17 | super(ctParam, inParam); 18 | } 19 | 20 | @Override 21 | public InputStream getInputStream() { 22 | InputStream inputStream = super.getInputStream(); 23 | inputStreams.add(inputStream); 24 | return inputStream; 25 | } 26 | 27 | public void closeAll() { 28 | inputStreams.forEach(this::close); 29 | } 30 | 31 | @SneakyThrows 32 | private void close(InputStream inputStream) { 33 | inputStream.close(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/apache/cxf/attachment/As4AttachmentImpl.java: -------------------------------------------------------------------------------- 1 | package org.apache.cxf.attachment; 2 | 3 | import org.apache.cxf.attachment.AttachmentImpl; 4 | 5 | public class As4AttachmentImpl extends AttachmentImpl { 6 | public As4AttachmentImpl(String idParam) { 7 | super(idParam); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/apache/cxf/attachment/As4AttachmentInInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.apache.cxf.attachment; 2 | 3 | import org.apache.cxf.common.logging.LogUtils; 4 | import org.apache.cxf.interceptor.AttachmentInInterceptor; 5 | import org.apache.cxf.interceptor.Fault; 6 | import org.apache.cxf.message.Message; 7 | import org.apache.cxf.phase.AbstractPhaseInterceptor; 8 | import org.apache.cxf.phase.Phase; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.logging.Logger; 15 | 16 | public class As4AttachmentInInterceptor extends AbstractPhaseInterceptor { 17 | 18 | private static final Logger LOG = LogUtils.getL7dLogger(AttachmentInInterceptor.class); 19 | 20 | private static final List TYPES = Collections.singletonList("multipart/related"); 21 | 22 | public As4AttachmentInInterceptor() { 23 | super(Phase.RECEIVE); 24 | } 25 | 26 | public void handleMessage(Message message) { 27 | if (isGET(message)) { 28 | LOG.fine("As4AttachmentInInterceptor skipped in HTTP GET method"); 29 | return; 30 | } 31 | if (message.getContent(InputStream.class) == null) { 32 | return; 33 | } 34 | 35 | String contentType = (String) message.get(Message.CONTENT_TYPE); 36 | if (AttachmentUtil.isTypeSupported(contentType, getSupportedTypes())) { 37 | As4AttachmentDeserializer ad = new As4AttachmentDeserializer(message, getSupportedTypes()); 38 | try { 39 | ad.initializeAttachments(); 40 | } catch (IOException e) { 41 | throw new Fault(e); 42 | } 43 | 44 | message.put(As4AttachmentDeserializer.class, ad); 45 | } 46 | } 47 | 48 | public void handleFault(Message messageParam) { 49 | } 50 | 51 | protected List getSupportedTypes() { 52 | return TYPES; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/org/apache/cxf/attachment/As4AttachmentUtil.java: -------------------------------------------------------------------------------- 1 | package org.apache.cxf.attachment; 2 | 3 | import org.apache.cxf.common.util.StringUtils; 4 | import org.apache.cxf.helpers.FileUtils; 5 | import org.apache.cxf.message.Attachment; 6 | 7 | import jakarta.activation.DataHandler; 8 | import jakarta.activation.DataSource; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public final class As4AttachmentUtil { 15 | 16 | private As4AttachmentUtil() { 17 | 18 | } 19 | 20 | static String getHeaderValue(List v) { 21 | if (v != null && !v.isEmpty()) { 22 | return v.get(0); 23 | } 24 | return null; 25 | } 26 | 27 | static String getHeaderValue(List v, String delim) { 28 | if (v != null && !v.isEmpty()) { 29 | return String.join(delim, v); 30 | } 31 | return null; 32 | } 33 | 34 | static String getHeader(Map> headers, String h) { 35 | return getHeaderValue(headers.get(h)); 36 | } 37 | 38 | static String getHeader(Map> headers, String h, String delim) { 39 | return getHeaderValue(headers.get(h), delim); 40 | } 41 | 42 | public static Attachment createAttachment(InputStream stream, Map> headers) 43 | throws IOException { 44 | 45 | String id = AttachmentUtil.cleanContentId(getHeader(headers, "Content-ID")); 46 | 47 | As4AttachmentImpl att = new As4AttachmentImpl(id); 48 | 49 | final String ct = getHeader(headers, "Content-Type"); 50 | String cd = getHeader(headers, "Content-Disposition"); 51 | String fileName = getContentDispositionFileName(cd); 52 | 53 | String encoding = null; 54 | 55 | for (Map.Entry> e : headers.entrySet()) { 56 | String name = e.getKey(); 57 | if ("Content-Transfer-Encoding".equalsIgnoreCase(name)) { 58 | encoding = getHeader(headers, name); 59 | if ("binary".equalsIgnoreCase(encoding)) { 60 | att.setXOP(true); 61 | } 62 | } 63 | att.setHeader(name, getHeaderValue(e.getValue())); 64 | } 65 | if (encoding == null) { 66 | encoding = "binary"; 67 | } 68 | InputStream ins = AttachmentUtil.decode(stream, encoding); 69 | if (ins != stream) { 70 | headers.remove("Content-Transfer-Encoding"); 71 | } 72 | DataSource source = new As4AttachmentDataSource(ct, ins); 73 | if (!StringUtils.isEmpty(fileName)) { 74 | ((As4AttachmentDataSource) source).setName(FileUtils.stripPath(fileName)); 75 | } 76 | att.setDataHandler(new DataHandler(source)); 77 | return att; 78 | } 79 | 80 | static String getContentDispositionFileName(String cd) { 81 | if (StringUtils.isEmpty(cd)) { 82 | return null; 83 | } 84 | ContentDisposition c = new ContentDisposition(cd); 85 | String s = c.getParameter("filename"); 86 | if (s == null) { 87 | s = c.getParameter("name"); 88 | } 89 | return s; 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/main/java/org/apache/cxf/attachment/As4DelegatingInputStream.java: -------------------------------------------------------------------------------- 1 | package org.apache.cxf.attachment; 2 | 3 | import org.apache.cxf.helpers.IOUtils; 4 | import org.apache.cxf.io.Transferable; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | public class As4DelegatingInputStream extends InputStream implements Transferable { 11 | private InputStream is; 12 | private As4AttachmentDeserializer deserializer; 13 | private boolean isClosed; 14 | 15 | As4DelegatingInputStream(InputStream is, As4AttachmentDeserializer ads) { 16 | this.is = is; 17 | deserializer = ads; 18 | } 19 | 20 | @Override 21 | public void close() throws IOException { 22 | IOUtils.consume(is); 23 | is.close(); 24 | if (!isClosed && deserializer != null) { 25 | deserializer.markClosed(this); 26 | } 27 | isClosed = true; 28 | } 29 | 30 | public void transferTo(File destinationFile) throws IOException { 31 | if (isClosed) { 32 | throw new IOException("Stream is closed"); 33 | } 34 | IOUtils.transferTo(is, destinationFile); 35 | } 36 | 37 | public boolean isClosed() { 38 | return isClosed; 39 | } 40 | 41 | public void setClosed(boolean closed) { 42 | this.isClosed = closed; 43 | } 44 | 45 | public int read() throws IOException { 46 | return this.is.read(); 47 | } 48 | 49 | @Override 50 | public int available() throws IOException { 51 | return this.is.available(); 52 | } 53 | 54 | @Override 55 | public synchronized void mark(int arg0) { 56 | this.is.mark(arg0); 57 | } 58 | 59 | @Override 60 | public boolean markSupported() { 61 | return this.is.markSupported(); 62 | } 63 | 64 | @Override 65 | public int read(byte[] bytes, int arg1, int arg2) throws IOException { 66 | return this.is.read(bytes, arg1, arg2); 67 | } 68 | 69 | @Override 70 | public int read(byte[] bytes) throws IOException { 71 | return this.is.read(bytes); 72 | } 73 | 74 | @Override 75 | public synchronized void reset() throws IOException { 76 | this.is.reset(); 77 | } 78 | 79 | @Override 80 | public long skip(long n) throws IOException { 81 | return this.is.skip(n); 82 | } 83 | 84 | public void setInputStream(InputStream inputStream) { 85 | this.is = inputStream; 86 | } 87 | 88 | 89 | public InputStream getInputStream() { 90 | return is; 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/javax.xml.ws.spi.Provider: -------------------------------------------------------------------------------- 1 | org.apache.cxf.jaxws.spi.ProviderImpl -------------------------------------------------------------------------------- /src/main/resources/eDeliveryAS4Policy.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/resources/eDeliveryAS4Policy_BST.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/resources/oxalis-as4-version.properties: -------------------------------------------------------------------------------- 1 | oxalis.version=${project.version} 2 | -------------------------------------------------------------------------------- /src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | oxalis.module.as4.common = { 2 | class = network.oxalis.as4.common.As4CommonModule 3 | } 4 | 5 | oxalis.module.as4.inbound = { 6 | class = network.oxalis.as4.inbound.As4InboundModule 7 | dependency = inbound.servlet 8 | } 9 | 10 | oxalis.module.as4.outbound = { 11 | class = network.oxalis.as4.outbound.As4OutboundModule 12 | dependency = outbound.lookup 13 | } 14 | 15 | defaults.transport.as4_peppol_v2 = { 16 | profile: peppol-transport-as4-v2_0 17 | sender: oxalis-as4 18 | weight: 9001 19 | } -------------------------------------------------------------------------------- /src/main/resources/signOnly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/xsd/bindings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/main/xsd/w3/datatypes.dtd: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 69 | 70 | 80 | 81 | 82 | 83 | 84 | 85 | 88 | 89 | 92 | 93 | 94 | 96 | 101 | 102 | 106 | 110 | 119 | 120 | 124 | 128 | 129 | 133 | 137 | 138 | 139 | 143 | 144 | 148 | 149 | 150 | 154 | 155 | 159 | 160 | 161 | 165 | 166 | 170 | 171 | 172 | 176 | 177 | 181 | 182 | 186 | 187 | 188 | 189 | 192 | 193 | 194 | 198 | 199 | 200 | 201 | 204 | -------------------------------------------------------------------------------- /src/main/xsd/w3/soap-envelope.xsd: -------------------------------------------------------------------------------- 1 | 17 | 18 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Elements replacing the wildcard MUST be namespace qualified, but can be in the targetNamespace 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Fault reporting structure 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 106 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 126 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /src/main/xsd/xmlsoap/envelope.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Prose in the spec does not specify that attributes are allowed on the Body element 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Fault reporting structure 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/common/DefaultMessageIdGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.common; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import org.powermock.core.classloader.annotations.PrepareForTest; 9 | import org.powermock.modules.junit4.PowerMockRunner; 10 | 11 | import java.util.UUID; 12 | 13 | import static org.powermock.api.mockito.PowerMockito.*; 14 | 15 | @RunWith(PowerMockRunner.class) 16 | @PrepareForTest(DefaultMessageIdGenerator.class) 17 | public class DefaultMessageIdGeneratorTest { 18 | 19 | private DefaultMessageIdGenerator generator; 20 | 21 | @Before 22 | public void setUp() { 23 | UUID fixedUUID = UUID.fromString("8196c8e2-820f-4aec-a1ca-288a4d1d4020"); 24 | 25 | mockStatic(UUID.class); 26 | when(UUID.randomUUID()).thenReturn(fixedUUID); 27 | 28 | generator = new DefaultMessageIdGenerator("seller.eu"); 29 | } 30 | 31 | @Test 32 | public void testGenerateByTransmissionRequest() { 33 | Assert.assertEquals("8196c8e2-820f-4aec-a1ca-288a4d1d4020@seller.eu", generator.generate()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/inbound/AS4StatusServletTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2018 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.inbound; 24 | 25 | import com.google.inject.AbstractModule; 26 | import com.google.inject.Guice; 27 | import com.google.inject.Injector; 28 | import com.google.inject.util.Modules; 29 | import network.oxalis.api.inbound.InboundService; 30 | import network.oxalis.commons.guice.GuiceModuleLoader; 31 | import network.oxalis.test.jetty.AbstractJettyServerTest; 32 | import org.testng.Assert; 33 | import org.testng.annotations.Test; 34 | 35 | import jakarta.servlet.http.HttpServletResponse; 36 | import java.net.HttpURLConnection; 37 | import java.net.URL; 38 | 39 | @Test 40 | public class AS4StatusServletTest extends AbstractJettyServerTest { 41 | 42 | @Override 43 | public Injector getInjector() { 44 | return Guice.createInjector( 45 | new As4InboundModule(), 46 | Modules.override(new GuiceModuleLoader()).with(new AbstractModule() { 47 | @Override 48 | protected void configure() { 49 | // bind(ReceiptPersister.class).toInstance((m, p) -> { 50 | // }); 51 | // bind(PayloadPersister.class).toInstance(temporaryFilePersister); 52 | bind(InboundService.class).toInstance(p -> {}); 53 | // bind(MessageIdGenerator.class).toInstance(new DefaultMessageIdGenerator("test.com")); 54 | } 55 | }) 56 | ); 57 | } 58 | 59 | @Test 60 | public void get() throws Exception { 61 | HttpURLConnection httpURLConnection = 62 | (HttpURLConnection) new URL("http://localhost:8080/as4/status").openConnection(); 63 | 64 | Assert.assertEquals(httpURLConnection.getResponseCode(), HttpServletResponse.SC_OK); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/inbound/As4InboundHandlerTest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import network.oxalis.as4.lang.OxalisAs4Exception; 4 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.PartInfo; 5 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.PartProperties; 6 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.PayloadInfo; 7 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Property; 8 | import org.testng.annotations.Test; 9 | 10 | import static org.testng.Assert.*; 11 | 12 | public class As4InboundHandlerTest { 13 | 14 | @Test() 15 | public void testValidateMessageId_withValidHref() throws Exception{ 16 | 17 | Property property = new Property(); 18 | property.setName("MimeType"); 19 | property.setValue("Dummy"); 20 | 21 | PartProperties partProperties = new PartProperties(); 22 | partProperties.getProperty().add(property); 23 | 24 | PartInfo partInfo = new PartInfo(); 25 | partInfo.setHref("cid:attachedPayload"); 26 | partInfo.setPartProperties(partProperties); 27 | 28 | PayloadInfo payloadInfo = new PayloadInfo(); 29 | payloadInfo.getPartInfo().add(partInfo); 30 | 31 | As4InboundHandler.validatePayloads(payloadInfo); 32 | } 33 | 34 | @Test( expectedExceptions = {OxalisAs4Exception.class} ) 35 | public void testValidateMessageId_withInvalidHref() throws Exception{ 36 | PartInfo partInfo = new PartInfo(); 37 | partInfo.setHref("http://difi.no"); 38 | 39 | PayloadInfo payloadInfo = new PayloadInfo(); 40 | payloadInfo.getPartInfo().add(partInfo); 41 | 42 | As4InboundHandler.validatePayloads(payloadInfo); 43 | 44 | fail(); 45 | } 46 | 47 | 48 | } -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/inbound/As4ServletTest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.inbound; 2 | 3 | import org.mockito.InjectMocks; 4 | import org.mockito.Mock; 5 | import org.mockito.MockitoAnnotations; 6 | import org.powermock.reflect.Whitebox; 7 | import org.testng.annotations.BeforeMethod; 8 | import org.testng.annotations.BeforeTest; 9 | 10 | import java.nio.file.Path; 11 | import java.security.KeyStore; 12 | 13 | public class As4ServletTest { 14 | 15 | @Mock 16 | private Path confFolder; 17 | 18 | private KeyStore trustStore = null; 19 | 20 | @InjectMocks 21 | private As4Servlet servlet = new As4Servlet(); 22 | 23 | 24 | private KeyStore generateEmptyKeyStore() { 25 | try { 26 | KeyStore ks = KeyStore.getInstance("jks"); 27 | ks.load(null, null); 28 | 29 | return ks; 30 | } catch (Exception e) { 31 | return null; 32 | } 33 | } 34 | 35 | @BeforeTest 36 | public void beforeTest() { 37 | MockitoAnnotations.initMocks(this); 38 | } 39 | 40 | @BeforeMethod 41 | public void beforeMethod() { 42 | trustStore = generateEmptyKeyStore(); 43 | Whitebox.setInternalState(servlet, "trustStore", trustStore); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/outbound/MessagingProviderTest_CEF_SBDH.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import network.oxalis.api.settings.Settings; 4 | import network.oxalis.as4.config.As4Conf; 5 | import network.oxalis.as4.util.PeppolConfiguration; 6 | 7 | import jakarta.inject.Named; 8 | import java.nio.file.Path; 9 | 10 | public class MessagingProviderTest_CEF_SBDH extends AbstractMessagingProviderTest { 11 | 12 | @Override 13 | protected String getPayloadPath() { 14 | return "/cef-sbd.xml"; 15 | } 16 | 17 | @Override 18 | protected PeppolConfiguration getPEPPOLOutboundConfiguration() { 19 | 20 | return new As4OutboundModule().getPeppolOutboundConfiguration(new Settings(){ 21 | @Override 22 | public String getString(As4Conf as4Conf) { 23 | return "cef-connectivity"; 24 | } 25 | 26 | @Override 27 | public int getInt(As4Conf as4Conf) { 28 | return 0; 29 | } 30 | 31 | @Override 32 | public Named getNamed(As4Conf key) { 33 | return null; 34 | } 35 | 36 | @Override 37 | public Path getPath(As4Conf key, Path path) { 38 | return null; 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | protected String getAction() { 45 | return "submitMessage"; 46 | } 47 | 48 | @Override 49 | protected String getServiceType() { 50 | return "e-delivery"; 51 | } 52 | 53 | @Override 54 | protected String getServiceValue() { 55 | return "http://ec.europa.eu/edelivery/services/connectivity-service"; 56 | } 57 | 58 | @Override 59 | protected String getPartyType() { 60 | return "urn:oasis:names:tc:ebcore:partyid-type:unregistered"; 61 | } 62 | 63 | @Override 64 | protected String getFinalRecipient() { 65 | return "urn:oasis:names:tc:ebcore:partyid-type:unregistered:c4"; 66 | } 67 | 68 | @Override 69 | protected String getOriginalSender() { 70 | return "urn:oasis:names:tc:ebcore:partyid-type:unregistered:c1"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/outbound/MessagingProviderTest_SIMPLE_SBDH.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.outbound; 2 | 3 | import network.oxalis.as4.util.PeppolConfiguration; 4 | 5 | public class MessagingProviderTest_SIMPLE_SBDH extends AbstractMessagingProviderTest { 6 | 7 | @Override 8 | protected String getPayloadPath() { 9 | return "/simple-sbd.xml"; 10 | } 11 | 12 | @Override 13 | protected PeppolConfiguration getPEPPOLOutboundConfiguration() { 14 | 15 | return new PeppolConfiguration(); 16 | } 17 | 18 | @Override 19 | protected String getAction() { 20 | return "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:www.cenbii.eu:transaction:biitrns010:ver2.0:extended:urn:www.peppol.eu:bis:peppol4a:ver2.0::2.1"; 21 | } 22 | 23 | @Override 24 | protected String getServiceType() { 25 | return "cenbii-procid-ubl"; 26 | } 27 | 28 | @Override 29 | protected String getServiceValue() { 30 | return "urn:www.cenbii.eu:profile:bii04:ver2.0"; 31 | } 32 | 33 | @Override 34 | protected String getPartyType() { 35 | return "urn:fdc:peppol.eu:2017:identifiers:ap"; 36 | } 37 | 38 | @Override 39 | protected String getFinalRecipient() { 40 | return "iso6523-actorid-upis::9908:810418052"; 41 | } 42 | 43 | @Override 44 | protected String getOriginalSender() { 45 | return "iso6523-actorid-upis::0088:oxalis"; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/util/AS4ErrorCodeTest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | import static org.testng.Assert.*; 6 | 7 | public class AS4ErrorCodeTest { 8 | 9 | @Test 10 | public void nameOf_validNames_Test(){ 11 | 12 | assertEquals(AS4ErrorCode.nameOf("EBMS:0303"), AS4ErrorCode.EBMS_0303); 13 | assertEquals(AS4ErrorCode.nameOf("EBMS:0004"), AS4ErrorCode.EBMS_0004); 14 | 15 | assertEquals(AS4ErrorCode.nameOf("ebms:0303"), AS4ErrorCode.EBMS_0303); 16 | assertEquals(AS4ErrorCode.nameOf("ebms:0004"), AS4ErrorCode.EBMS_0004); 17 | } 18 | 19 | @Test 20 | public void nameOf_invalidNames_Test(){ 21 | 22 | assertNull(AS4ErrorCode.nameOf("EBMS:9999")); 23 | assertNull(AS4ErrorCode.nameOf("ebms:9999")); 24 | } 25 | 26 | @Test 27 | public void toString_Test(){ 28 | 29 | assertEquals(AS4ErrorCode.EBMS_0004.toString(), "EBMS:0004"); 30 | assertEquals(AS4ErrorCode.EBMS_0303.toString(), "EBMS:0303"); 31 | } 32 | 33 | @Test 34 | public void toString_and_getErrorCode_equality_Test(){ 35 | 36 | assertEquals(AS4ErrorCode.EBMS_0004.toString(), AS4ErrorCode.EBMS_0004.getErrorCode()); 37 | assertEquals(AS4ErrorCode.EBMS_0303.toString(), AS4ErrorCode.EBMS_0303.getErrorCode()); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/util/CompressionUtilTest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.testng.Assert; 5 | import org.testng.annotations.Test; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.InputStream; 9 | import java.util.Random; 10 | import java.util.zip.GZIPInputStream; 11 | 12 | public class CompressionUtilTest { 13 | @Test 14 | public void simple() throws Exception { 15 | byte[] before = "Lorem ipsum dolor sit amet".getBytes(); 16 | InputStream sourceStream = new ByteArrayInputStream(before); 17 | InputStream compressedStream = new CompressionUtil().getCompressedStream(sourceStream); 18 | try (GZIPInputStream decompressedStream = new GZIPInputStream(compressedStream)) { 19 | byte[] after = IOUtils.toByteArray(decompressedStream); 20 | Assert.assertEquals(before, after); 21 | } 22 | } 23 | 24 | @Test 25 | public void cachedInTempFile() throws Exception { 26 | byte[] before = new byte[1024 * 1024]; 27 | new Random().nextBytes(before); 28 | InputStream sourceStream = new ByteArrayInputStream(before); 29 | InputStream compressedStream = new CompressionUtil().getCompressedStream(sourceStream); 30 | try (GZIPInputStream decompressedStream = new GZIPInputStream(compressedStream)) { 31 | byte[] after = IOUtils.toByteArray(decompressedStream); 32 | Assert.assertEquals(before, after); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/util/MessageIdUtilTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) 3 | * 4 | * Licensed under the EUPL, Version 1.1 or – as soon they 5 | * will be approved by the European Commission - subsequent 6 | * versions of the EUPL (the "Licence"); 7 | * 8 | * You may not use this work except in compliance with the Licence. 9 | * 10 | * You may obtain a copy of the Licence at: 11 | * 12 | * https://joinup.ec.europa.eu/community/eupl/og_page/eupl 13 | * 14 | * Unless required by applicable law or agreed to in 15 | * writing, software distributed under the Licence is 16 | * distributed on an "AS IS" basis, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 18 | * express or implied. 19 | * See the Licence for the specific language governing 20 | * permissions and limitations under the Licence. 21 | */ 22 | 23 | package network.oxalis.as4.util; 24 | 25 | import org.testng.Assert; 26 | import org.testng.annotations.Test; 27 | 28 | public class MessageIdUtilTest { 29 | 30 | @Test 31 | public void testVerify() { 32 | Assert.assertTrue(MessageIdUtil.verify("1060501332.515.1528302064500@de77cf5d0088")); 33 | Assert.assertTrue(MessageIdUtil.verify("OpenPEPPOL-06062018160101+0300-0447@APP_1000000200_APP_2000000300")); 34 | Assert.assertTrue(MessageIdUtil.verify("a397-ca2a711dff5b@seller.eu")); 35 | Assert.assertTrue(MessageIdUtil.verify("8196c8e2-820f-4aec-a1ca-288a4d1d4020@seller.eu")); 36 | 37 | Assert.assertFalse(MessageIdUtil.verify("1060501332..515.1528302064500@de77cf5d0088")); 38 | Assert.assertFalse(MessageIdUtil.verify(" 1060501332.515.1528302064500@de77cf5d0088")); 39 | Assert.assertFalse(MessageIdUtil.verify("1060501332.515.1528302064500_de77cf5d0088")); 40 | 41 | Assert.assertTrue(MessageIdUtil.verify("Aa1!#$%&'*+-/=?^_`{|}~.Aa1!#$%&'*+-/=?^_`{|}~@Aa1!#$%&'*+-/=?^_`{|}~.Aa1!#$%&'*+-/=?^_`{|}~")); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/as4/util/TransmissionRequestUtilTest.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.as4.util; 2 | 3 | import network.oxalis.as4.common.As4MessageProperty; 4 | import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; 5 | import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; 6 | import network.oxalis.vefa.peppol.common.model.Scheme; 7 | import org.testng.Assert; 8 | import org.testng.annotations.Test; 9 | 10 | public class TransmissionRequestUtilTest { 11 | 12 | private static String PROVIDED_VALUE = "provided value"; 13 | private static String PROVIDED_SCHEME = "provided scheme"; 14 | 15 | @Test 16 | public void testTranslateDocumentTypeToAction_defaultScheme() { 17 | String result = TransmissionRequestUtil.translateDocumentTypeToAction(DocumentTypeIdentifier.of(PROVIDED_VALUE)); 18 | Assert.assertEquals(DocumentTypeIdentifier.DEFAULT_SCHEME + "::" + PROVIDED_VALUE, result); 19 | } 20 | 21 | @Test 22 | public void testTranslateDocumentTypeToAction_nullScheme() { 23 | String result = TransmissionRequestUtil.translateDocumentTypeToAction(DocumentTypeIdentifier.of(PROVIDED_VALUE, null)); 24 | Assert.assertEquals(PROVIDED_VALUE, result); 25 | } 26 | 27 | @Test 28 | public void testTranslateDocumentTypeToAction_providedScheme() { 29 | String result = TransmissionRequestUtil.translateDocumentTypeToAction(DocumentTypeIdentifier.of(PROVIDED_VALUE, Scheme.of(PROVIDED_SCHEME))); 30 | Assert.assertEquals(PROVIDED_SCHEME + "::" + PROVIDED_VALUE, result); 31 | } 32 | 33 | 34 | @Test 35 | public void testTranslateParticipantIdentifierToRecipient_defaultScheme() { 36 | As4MessageProperty result = TransmissionRequestUtil.toAs4MessageProperty("hello", ParticipantIdentifier.of(PROVIDED_VALUE)); 37 | 38 | Assert.assertEquals("hello", result.getName()); 39 | Assert.assertEquals("iso6523-actorid-upis", result.getType()); 40 | Assert.assertEquals(PROVIDED_VALUE, result.getValue()); 41 | } 42 | 43 | @Test 44 | public void testTranslateParticipantIdentifierToRecipient_nullScheme() { 45 | As4MessageProperty result = TransmissionRequestUtil.toAs4MessageProperty("hello", ParticipantIdentifier.of(PROVIDED_VALUE, null)); 46 | 47 | Assert.assertEquals("hello", result.getName()); 48 | Assert.assertNull(result.getType()); 49 | Assert.assertEquals(PROVIDED_VALUE, result.getValue()); 50 | } 51 | 52 | @Test 53 | public void testTranslateParticipantIdentifierToRecipient_providedScheme() { 54 | As4MessageProperty result = TransmissionRequestUtil.toAs4MessageProperty("hello", ParticipantIdentifier.of(PROVIDED_VALUE, Scheme.of(PROVIDED_SCHEME))); 55 | 56 | Assert.assertEquals("hello", result.getName()); 57 | Assert.assertEquals(PROVIDED_SCHEME, result.getType()); 58 | Assert.assertEquals(PROVIDED_VALUE, result.getValue()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/outbound/transmission/DefaultTransmissionRequestFacade.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.outbound.transmission; 2 | 3 | import network.oxalis.api.outbound.TransmissionMessage; 4 | import network.oxalis.api.outbound.TransmissionRequest; 5 | import network.oxalis.api.tag.Tag; 6 | import network.oxalis.vefa.peppol.common.model.Endpoint; 7 | import network.oxalis.vefa.peppol.common.model.Header; 8 | 9 | import java.io.InputStream; 10 | import java.io.Serializable; 11 | 12 | public class DefaultTransmissionRequestFacade implements TransmissionRequest, Serializable { 13 | 14 | private static final long serialVersionUID = -4542158937465140099L; 15 | 16 | private DefaultTransmissionRequest defaultTransmissionRequest; 17 | 18 | public DefaultTransmissionRequestFacade(TransmissionMessage transmissionMessage, Endpoint endpoint) { 19 | defaultTransmissionRequest = new DefaultTransmissionRequest(transmissionMessage, endpoint); 20 | } 21 | 22 | @Override 23 | public Endpoint getEndpoint() { 24 | return defaultTransmissionRequest.getEndpoint(); 25 | } 26 | 27 | @Override 28 | public Header getHeader() { 29 | return defaultTransmissionRequest.getHeader(); 30 | } 31 | 32 | @Override 33 | public InputStream getPayload() { 34 | return defaultTransmissionRequest.getPayload(); 35 | } 36 | 37 | @Override 38 | public Tag getTag() { 39 | return defaultTransmissionRequest.getTag(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/network/oxalis/outbound/transmission/MessagingProviderFacade.java: -------------------------------------------------------------------------------- 1 | package network.oxalis.outbound.transmission; 2 | 3 | import network.oxalis.api.outbound.TransmissionRequest; 4 | import network.oxalis.as4.api.MessageIdGenerator; 5 | import network.oxalis.as4.lang.OxalisAs4TransmissionException; 6 | import network.oxalis.as4.outbound.DefaultActionProvider; 7 | import network.oxalis.as4.outbound.MessagingProvider; 8 | import network.oxalis.as4.util.PeppolConfiguration; 9 | import org.apache.cxf.message.Attachment; 10 | import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Messaging; 11 | 12 | import java.security.cert.X509Certificate; 13 | import java.util.Collection; 14 | 15 | public class MessagingProviderFacade { 16 | 17 | private MessagingProvider messagingProvider; 18 | 19 | public MessagingProviderFacade(X509Certificate senderCert, MessageIdGenerator messageIdGenerator, PeppolConfiguration peppolConfiguration) { 20 | messagingProvider = new MessagingProvider( 21 | senderCert, 22 | messageIdGenerator, 23 | peppolConfiguration, 24 | new DefaultActionProvider()); 25 | } 26 | 27 | public Messaging createMessagingHeader(TransmissionRequest request, Collection attachments) throws OxalisAs4TransmissionException { 28 | return messagingProvider.createMessagingHeader(request, attachments); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/resources/cef-sbd.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.0 5 | 6 | 7 | 8 | urn:oasis:names:tc:ebcore:partyid-type:unregistered:C1 9 | 10 | 11 | 12 | 13 | urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4 14 | 15 | 16 | 17 | 18 | 19 | NONE 20 | 1.0 21 | 555bcb4c-940b-4694-9b90-d9b0ae1e937b 22 | CEF Connectivity test 23 | 2019-10-30T11:20:05.304+02:00 24 | 25 | 26 | 27 | 28 | 29 | 30 | DOCUMENTID 31 | 32 | 33 | 34 | submitMessage 35 | 36 | 37 | 38 | 39 | 40 | PROCESSID 41 | 42 | e-delivery 43 | 44 | http://ec.europa.eu/edelivery/services/connectivity-service 45 | 46 | 47 | 48 | eDelivery AS4 Connectivity test. Sending Message 49 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 | 26 | 27 | 28 | 29 | %d %p [%c] %m %n 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/test/resources/oxalis_home/eutest_gateway_truststore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/src/test/resources/oxalis_home/eutest_gateway_truststore.jks -------------------------------------------------------------------------------- /src/test/resources/oxalis_home/fake-oxalis.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/src/test/resources/oxalis_home/fake-oxalis.conf -------------------------------------------------------------------------------- /src/test/resources/oxalis_home/peppol_trust_g2_and_g3.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxalisCommunity/Oxalis-AS4/5aecaca2e2f3b92c56b59e645f58a0117908379f/src/test/resources/oxalis_home/peppol_trust_g2_and_g3.jks -------------------------------------------------------------------------------- /src/test/resources/reference.conf: -------------------------------------------------------------------------------- 1 | brave.reporter = noop 2 | oxalis.statistics.service = noop 3 | oxalis.persister.payload = noop 4 | oxalis.persister.receipt = noop 5 | 6 | oxalis.http.pool.max_route = 10 7 | 8 | oxalis.truststore.path=peppol_trust_g2_and_g3.jks 9 | oxalis.truststore.password=changeit 10 | -------------------------------------------------------------------------------- /src/test/resources/simple-sbd.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.0 5 | 6 | 0088:oxalis 7 | 8 | 9 | 9908:810418052 10 | 11 | 12 | urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 13 | 2.1 14 | 555bcb4c-940b-4694-9b90-d9b0ae1e937b 15 | Invoice 16 | 2016-10-19T11:20:05.304+02:00 17 | 18 | 19 | 20 | DOCUMENTID 21 | urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:www.cenbii.eu:transaction:biitrns010:ver2.0:extended:urn:www.peppol.eu:bis:peppol4a:ver2.0::2.1 22 | 23 | 24 | PROCESSID 25 | urn:www.cenbii.eu:profile:bii04:ver2.0 26 | 27 | 28 | 29 | 30 | --------------------------------------------------------------------------------