Unable to create ticket article through API

Infos:

  • Used Zammad version: 3.4
  • Used Zammad installation source: docker

Hello,

after upgrading to 3.4, I can’t create article for ticket with X-On-Behalf-Of header. (upgrading from 3.3 or 3.2 - not sure). The requests ends with NotAuthorizedError. Did I miss something or is it bug?

Thanks.

Steps to reproduce the behavior:

  • create ticket with API and X-On-Behalf-Of header (using zammad-api-client-php)
  • create ticket article for this ticket with X-On-Behalf-Of header (the same customer_id)

As far as I’m aware nothing in this context has changed in the last time.
Please provide exact error messages and your production log on the moment (with full traceback!) you’re trying to add a ticket through API.

Hi,

I have exactly the same problem, trying to create a ticket article with X-On-Behalf-Of header and getting a 401 Unauthorized.

Is this a Zammad issue or a PHP client issue ?

well, my request for more information stays the same.

Sorry for the delay. I tested it again with multiple versions on test instance in Docker. I upgraded Zammad from 3.2 to 3.3 and 3.4. Everything tested with the same ticket + with new one - the same database, just upgraded and the problem is in the 3.4 version only.

Error message is just HTTP status 401 - Not authorized.

Thank you.

I, [2020-08-29T13:25:44.236537 #1-46970070527600]  INFO -- : Started POST "/api/v1/ticket_articles?expand=1" for 172.23.0.1 at 2020-08-29 13:25:44 +0000
I, [2020-08-29T13:25:44.239920 #1-46970070527600]  INFO -- : Processing by TicketArticlesController#create as JSON
I, [2020-08-29T13:25:44.240084 #1-46970070527600]  INFO -- :   Parameters: {"ticket_id"=>35, "body"=>"[FILTERED]", "type"=>"web", "expand"=>"1"}
I, [2020-08-29T13:25:44.256460 #1-46970070527600]  INFO -- : Enqueued UserDeviceLogJob (Job ID: a6aeec5a-4166-4de7-97f0-4691df2cc3b5) to DelayedJob(default) with arguments: "GuzzleHttp/6.5.3 curl/7.67.0 PHP/7.4.2", "172.23.0.1", 4, nil, "token_auth"
I, [2020-08-29T13:25:44.263553 #1-46970070527600]  INFO -- : not allowed to create? this #<Ticket id: 35, group_id: 1, priority_id: 2, state_id: 1, organization_id: nil, number: "57035", title: "test", owner_id: 1, customer_id: 8, note: nil, first_response_at: nil, first_response_escalation_at: nil, first_response_in_min: nil, first_response_diff_in_min: nil, close_at: nil, close_escalation_at: nil, close_in_min: nil, close_diff_in_min: nil, update_escalation_at: nil, update_in_min: nil, update_diff_in_min: nil, last_contact_at: "2020-08-29 13:22:27", last_contact_agent_at: nil, last_contact_customer_at: "2020-08-29 13:22:27", last_owner_update_at: nil, create_article_type_id: 11, create_article_sender_id: 2, article_count: 3, escalation_at: nil, pending_time: nil, type: nil, time_unit: nil, preferences: {}, updated_by_id: 8, created_by_id: 8, created_at: "2020-08-29 13:22:27", updated_at: "2020-08-29 13:22:50", serviceid: "", authorized: true, subjectid: "27"> (Pundit::NotAuthorizedError)
/usr/local/bundle/gems/pundit-2.0.1/lib/pundit.rb:221:in `authorize'
/opt/zammad/app/controllers/application_controller/authorizes.rb:8:in `authorize!'
/opt/zammad/app/controllers/ticket_articles_controller.rb:81:in `create'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/abstract_controller/base.rb:194:in `process_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal/rendering.rb:30:in `process_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/abstract_controller/callbacks.rb:42:in `block in process_action'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/callbacks.rb:109:in `block in run_callbacks'
/opt/zammad/app/controllers/application_controller/has_secure_content_security_policy_for_downloads.rb:18:in `block (4 levels) in <module:HasSecureContentSecurityPolicyForDownloads>'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/notifications.rb:180:in `subscribed'
/opt/zammad/app/controllers/application_controller/has_secure_content_security_policy_for_downloads.rb:17:in `block (3 levels) in <module:HasSecureContentSecurityPolicyForDownloads>'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/notifications.rb:180:in `subscribed'
/opt/zammad/app/controllers/application_controller/has_secure_content_security_policy_for_downloads.rb:16:in `block (2 levels) in <module:HasSecureContentSecurityPolicyForDownloads>'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/callbacks.rb:118:in `instance_exec'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/callbacks.rb:118:in `block in run_callbacks'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/callbacks.rb:136:in `run_callbacks'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/abstract_controller/callbacks.rb:41:in `process_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal/rescue.rb:22:in `process_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/notifications.rb:168:in `block in instrument'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/notifications/instrumenter.rb:23:in `instrument'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/notifications.rb:168:in `instrument'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal/instrumentation.rb:32:in `process_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal/params_wrapper.rb:256:in `process_action'
/usr/local/bundle/gems/activerecord-5.2.4.3/lib/active_record/railties/controller_runtime.rb:24:in `process_action'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/abstract_controller/base.rb:134:in `process'
/usr/local/bundle/gems/actionview-5.2.4.3/lib/action_view/rendering.rb:32:in `process'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal.rb:191:in `dispatch'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_controller/metal.rb:252:in `dispatch'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/routing/route_set.rb:52:in `dispatch'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/routing/route_set.rb:34:in `serve'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/journey/router.rb:52:in `block in serve'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/journey/router.rb:35:in `each'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/journey/router.rb:35:in `serve'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/routing/route_set.rb:840:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:420:in `call_app!'
/usr/local/bundle/gems/omniauth-saml-1.10.1/lib/omniauth/strategies/saml.rb:89:in `other_phase'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:190:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/usr/local/bundle/gems/omniauth-1.9.0/lib/omniauth/builder.rb:64:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/conditional_get.rb:40:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/head.rb:12:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/http/content_security_policy.rb:18:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/cookies.rb:670:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/callbacks.rb:98:in `run_callbacks'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/debug_exceptions.rb:61:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
/usr/local/bundle/gems/railties-5.2.4.3/lib/rails/rack/logger.rb:38:in `call_app'
/usr/local/bundle/gems/railties-5.2.4.3/lib/rails/rack/logger.rb:28:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/request_id.rb:27:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/method_override.rb:24:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'
/usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
/usr/local/bundle/gems/actionpack-5.2.4.3/lib/action_dispatch/middleware/executor.rb:14:in `call'
/usr/local/bundle/gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
/usr/local/bundle/gems/railties-5.2.4.3/lib/rails/engine.rb:524:in `call'
/usr/local/bundle/gems/puma-3.12.6/lib/puma/configuration.rb:227:in `call'
/usr/local/bundle/gems/puma-3.12.6/lib/puma/server.rb:706:in `handle_request'
/usr/local/bundle/gems/puma-3.12.6/lib/puma/server.rb:476:in `process_client'
/usr/local/bundle/gems/puma-3.12.6/lib/puma/server.rb:334:in `block in run'
/usr/local/bundle/gems/puma-3.12.6/lib/puma/thread_pool.rb:135:in `block in spawn_thread'
/usr/local/bundle/gems/logging-2.2.2/lib/logging/diagnostic_context.rb:474:in `block in create_with_logging_context'
I, [2020-08-29T13:25:44.264060 #1-46970070527600]  INFO -- : Exceptions::NotAuthorized (Exceptions::NotAuthorized)

I, [2020-08-29T13:25:44.264457 #1-46970070527600]  INFO -- : Completed 401 Unauthorized in 24ms (Views: 0.1ms | ActiveRecord: 8.8ms)

Another to me now relevant question:
How are you authenticating? Are you using basic authentication or API access token?

If you’re using the API access token, can you temporary try to use basic authentication to see if the problem persists?

By the way:
This problem should also appear if you’re query normal information from Zammad, e.g. /api/v1/users/me

With access token. I tried basic authentication and it works. I have access token with ticket.agent permission only. So next I tried new token with all permissions which are available for this user (admin.user, ticket.agent, user_preferences.access_token, user_preferences.avatar, user_preferences.calendar, user_preferences.device, user_preferences.language, user_preferences.linked_accounts, user_preferences.notifications, user_preferences.out_of_office, user_preferences.password) and still not working.

But request to /api/v1/users/me works with access token (and X-On-Behalf-Of header).

We do have the same problem. We can reproduce the problem by simply using curl and all API calls we tried except creating new tickets is working just fine.

We have enabled all available permissions for the used token.

The API documentation at https://docs.zammad.org/en/latest/api/ticket.html states that the permissions ticket.agent and ticket.customer are required to create tickets over the API. Those two don’t appear in the list when we create API tokens. And probably that’s the reason why the token authentication doesn’t allow us to do that.

If that’s true, what are the requirements to get those two permissions into the list for API tokens?

When creating tickets or their articles via API-Token and on-behalf of someone, you’ll need admin.user permission. This should be enough - however, the affected user needs ticket.agent (if agent) with fitting permissions.

The token permission looks like so:
image


I can create tickets on behalf without any issues.
Please however note that you need to provide an article with ticket creation.

I don’t know if the php api libary does something super special or different here.
You can cross check by using curl with on behalf:

curl --location --request POST 'https://X.zammad.com/api/v1/tickets' \
--header 'X-On-Behalf-Of: 8' \
--header 'Authorization: Bearer XYZ' \
--header 'Content-Type: application/json' \
--data-raw '{
    "group_id": 1,
    "priority_id": 2,
    "state_id": 1,
    "organization_id": 1,
    "title": "API-Test!",
    "owner_id": 1,
    "customer_id": 8,
    "note": null,
    "first_response_at": null,
    "first_response_escalation_at": null,
    "first_response_in_min": null,
    "first_response_diff_in_min": null,
    "close_at": null,
    "close_escalation_at": null,
    "close_in_min": null,
    "close_diff_in_min": null,
    "update_escalation_at": null,
    "update_in_min": null,
    "update_diff_in_min": null,
    "last_contact_at": "2020-05-20T06:32:23.991Z",
    "last_contact_agent_at": null,
    "last_contact_customer_at": "2020-05-20T06:32:23.991Z",
    "last_owner_update_at": null,
    "escalation_at": null,
    "pending_time": null,
    "time_unit": null,
    "updated_by_id": 8,
    "created_by_id": 8,
    "created_at": "2020-05-20T06:32:23.808Z",
    "updated_at": "2020-05-20T06:32:24.019Z",
    "article": {
        "to": "Good Guy <agent@example.net>",
        "cc": "",
        "from": "Evil Guy <evil@example.com>", 
        "subject": "some subject",
        "body": "huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
        "content_type": "text/html",
        "type": "email",
        "sender": "Customer",
        "internal": false,
        "preferences": {},
        "created_at": "2020-05-20T06:32:23.808Z"
    }
}'

If the curl command works, it’s not a direct Zammad bug, but tied to the api libary for php.
In this case the production log should hold a hint what went wrong. This is also the error you’ll need in order to be able to create a bug report on the fitting repo.

I hope this helps

Thanks @MrGeneration for the curl example. Originally, it didn’t work but then I checked the token again. It seems that the ticket.agent permission was missing although I was sure I set them all. I also tried with and without on-behalf-if and both works with curl.

So, next I will verify the difference to the PHP library and report back what the outcome will be.

My bad sorry.
You could be affected by this issue: https://github.com/zammad/zammad/issues/3186

Possibly, not 100% sure.

Fortunately not. I’ve now tried also with the PHP library and it works like a charm. The problem has only been the missing permission ticket.agent and the curl request can be reduced to this and still works:

curl --location --request POST 'https://zammad.example.com/api/v1/tickets' \
--header 'Authorization: Token token=MYTOKEN' \
--header 'Content-Type: application/json' \
--header 'X-On-Behalf-Of: 706' \
--data-raw '{
    "group": "Users",
    "title": "API-Test!",
    "customer": "706",
    "article": {
      "subject": "some subject",
      "body": "huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
      "content_type": "text/html",
      "type": "note",
      "internal": false
    }
}'

Hope this helps others in this thread too?