mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Merge remote-tracking branch 'origin/dev' into merge-release/17.5-20260609045502
This commit is contained in:
+18
-1
@@ -12,6 +12,7 @@ OpenProject offers different APIs:
|
||||
* SCIM (System for Cross-domain Identity Management)
|
||||
* MCP (Model Context Protocol)
|
||||
* BCF API v2.1 api targeted towards BIM use cases
|
||||
* .well-known endpoints
|
||||
|
||||
Please note that we intend to keep this specification as accurate and stable as possible, however work on APIs is still ongoing
|
||||
and not all resources and actions in OpenProject are yet accessible through the APIs.
|
||||
@@ -26,7 +27,7 @@ The API v3 is a general purpose API supporting multiple use cases.
|
||||
While by no means complete, a whole lot of different scenarios can be automatized which otherwise would have to be carried out by hand via the UI.
|
||||
Examples for this include managing work packages, projects and users.
|
||||
|
||||
We strive to maintain backward compatibility with this API in our stable OpenProject releases whenever possible.
|
||||
We strive to maintain backward compatibility with this API in our stable OpenProject releases whenever possible.
|
||||
|
||||
➔ [Go to OpenProject API](./introduction/)
|
||||
|
||||
@@ -53,3 +54,19 @@ This API supports BCF management in the context of BIM projects.
|
||||
While this API supports way less use cases than the more generic *API v3* it is compatible with the generic specification of a BCF API as [defined by the standard](https://github.com/buildingSMART/BCF-API/blob/release_2_1/README.md). Clients implementing the specification can manage topics and viewpoints.
|
||||
|
||||
➔ [Go to BCF API](./bcf-rest-api/)
|
||||
|
||||
## .well-known endpoints
|
||||
|
||||
Each OpenProject installation exposes some endpoints under the `/.well-known/` path:
|
||||
|
||||
* `/.well-known/oauth-authorization-server`: [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414): OAuth 2.0 Authorization Server Metadata
|
||||
* `/.well-known/oauth-protected-resource`: [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728): OAuth 2.0 Protected Resource Metadata
|
||||
* `/.well-known/openproject-metadata`: Exposing non-confidential metadata about the OpenProject installation
|
||||
|
||||
### OpenProject Metadata
|
||||
|
||||
The `/.well-known/openproject-metadata` endpoint exposes some non-confidential metadata about the OpenProject instance in JSON format. This endpoint is accessible without authentication.
|
||||
|
||||
The following keys are exposed:
|
||||
|
||||
* `installation_uuid`: A unique identifier that's different per installation of OpenProject
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Schema: MeetingOutcomeCollectionModel
|
||||
---
|
||||
type: object
|
||||
required:
|
||||
- _type
|
||||
- count
|
||||
- total
|
||||
- _embedded
|
||||
- _links
|
||||
properties:
|
||||
_type:
|
||||
type: string
|
||||
enum:
|
||||
- Collection
|
||||
count:
|
||||
type: integer
|
||||
total:
|
||||
type: integer
|
||||
_embedded:
|
||||
type: object
|
||||
required:
|
||||
- elements
|
||||
properties:
|
||||
elements:
|
||||
type: array
|
||||
items:
|
||||
$ref: './meeting_outcome_model.yml'
|
||||
_links:
|
||||
type: object
|
||||
required:
|
||||
- self
|
||||
properties:
|
||||
self:
|
||||
allOf:
|
||||
- $ref: './link.yml'
|
||||
- description: |-
|
||||
The link to this outcome collection resource
|
||||
|
||||
**Resource**: MeetingOutcomeCollection
|
||||
@@ -0,0 +1,69 @@
|
||||
# Schema: MeetingOutcomeModel
|
||||
---
|
||||
type: object
|
||||
required:
|
||||
- _type
|
||||
- id
|
||||
- kind
|
||||
- createdAt
|
||||
- updatedAt
|
||||
properties:
|
||||
_type:
|
||||
type: string
|
||||
enum:
|
||||
- MeetingOutcome
|
||||
id:
|
||||
type: integer
|
||||
description: Identifier of this outcome
|
||||
minimum: 1
|
||||
kind:
|
||||
type: string
|
||||
description: |-
|
||||
The type of this outcome. Possible values:
|
||||
|
||||
- *information*: an informational outcome with notes
|
||||
- *work_package*: an outcome linked to a work package
|
||||
enum:
|
||||
- information
|
||||
- work_package
|
||||
notes:
|
||||
$ref: "./formattable.yml"
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Time of creation.
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Time of the most recent change.
|
||||
_links:
|
||||
type: object
|
||||
properties:
|
||||
self:
|
||||
allOf:
|
||||
- $ref: './link.yml'
|
||||
- description: |-
|
||||
This outcome
|
||||
|
||||
**Resource**: MeetingOutcome
|
||||
author:
|
||||
allOf:
|
||||
- $ref: './link.yml'
|
||||
- description: |-
|
||||
The user who created this outcome
|
||||
|
||||
**Resource**: User
|
||||
agendaItem:
|
||||
allOf:
|
||||
- $ref: './link.yml'
|
||||
- description: |-
|
||||
The agenda item this outcome belongs to
|
||||
|
||||
**Resource**: MeetingAgendaItem
|
||||
workPackage:
|
||||
allOf:
|
||||
- $ref: './link.yml'
|
||||
- description: |-
|
||||
The linked work package (for work_package type outcomes)
|
||||
|
||||
**Resource**: WorkPackage
|
||||
@@ -0,0 +1,26 @@
|
||||
# Schema: MeetingOutcomeWriteModel
|
||||
---
|
||||
type: object
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
description: |-
|
||||
The type of this outcome. Possible values:
|
||||
|
||||
- *information*: an informational outcome with notes
|
||||
- *work_package*: an outcome linked to a work package
|
||||
enum:
|
||||
- information
|
||||
- work_package
|
||||
notes:
|
||||
$ref: "./formattable.yml"
|
||||
_links:
|
||||
type: object
|
||||
properties:
|
||||
workPackage:
|
||||
allOf:
|
||||
- $ref: "./link.yml"
|
||||
- description: |-
|
||||
The linked work package (for work_package type outcomes)
|
||||
|
||||
**Resource**: WorkPackage
|
||||
@@ -272,16 +272,32 @@ paths:
|
||||
"/api/v3/meetings/{id}":
|
||||
"$ref": "./paths/meeting.yml"
|
||||
"/api/v3/meetings/{id}/agenda_items":
|
||||
"$ref": "./paths/meeting_agenda_items.yml"
|
||||
"$ref": "./paths/meeting_agenda_items_by_meeting.yml"
|
||||
"/api/v3/meetings/{meeting_id}/agenda_items/{id}":
|
||||
"$ref": "./paths/meeting_agenda_item_by_meeting.yml"
|
||||
"/api/v3/meeting_agenda_items":
|
||||
"$ref": "./paths/meeting_agenda_items.yml"
|
||||
"/api/v3/meeting_agenda_items/{id}":
|
||||
"$ref": "./paths/meeting_agenda_item.yml"
|
||||
"/api/v3/meetings/{meeting_id}/agenda_items/{agenda_item_id}/outcomes":
|
||||
"$ref": "./paths/meeting_agenda_item_outcomes.yml"
|
||||
"/api/v3/meetings/{meeting_id}/agenda_items/{agenda_item_id}/outcomes/{id}":
|
||||
"$ref": "./paths/meeting_agenda_item_outcome.yml"
|
||||
"/api/v3/meeting_outcomes":
|
||||
"$ref": "./paths/meeting_outcomes.yml"
|
||||
"/api/v3/meeting_outcomes/{id}":
|
||||
"$ref": "./paths/meeting_outcome.yml"
|
||||
"/api/v3/meetings/{id}/attachments":
|
||||
"$ref": "./paths/meeting_attachments.yml"
|
||||
"/api/v3/meetings/{id}/form":
|
||||
"$ref": "./paths/meeting_form.yml"
|
||||
"/api/v3/meetings/{id}/sections":
|
||||
"$ref": "./paths/meeting_sections.yml"
|
||||
"$ref": "./paths/meeting_sections_by_meeting.yml"
|
||||
"/api/v3/meetings/{meeting_id}/sections/{id}":
|
||||
"$ref": "./paths/meeting_section_by_meeting.yml"
|
||||
"/api/v3/meeting_sections":
|
||||
"$ref": "./paths/meeting_sections.yml"
|
||||
"/api/v3/meeting_sections/{id}":
|
||||
"$ref": "./paths/meeting_section.yml"
|
||||
"/api/v3/meetings/form":
|
||||
"$ref": "./paths/meetings_form.yml"
|
||||
@@ -892,6 +908,12 @@ components:
|
||||
"$ref": "./components/schemas/meeting_agenda_item_write_model.yml"
|
||||
MeetingAgendaItemCollectionModel:
|
||||
"$ref": "./components/schemas/meeting_agenda_item_collection_model.yml"
|
||||
MeetingOutcomeCollectionModel:
|
||||
"$ref": "./components/schemas/meeting_outcome_collection_model.yml"
|
||||
MeetingOutcomeModel:
|
||||
"$ref": "./components/schemas/meeting_outcome_model.yml"
|
||||
MeetingOutcomeWriteModel:
|
||||
"$ref": "./components/schemas/meeting_outcome_write_model.yml"
|
||||
MeetingCollectionModel:
|
||||
"$ref": "./components/schemas/meeting_collection_model.yml"
|
||||
MeetingModel:
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
# /api/v3/meetings/{meeting_id}/agenda_items/{id}
|
||||
# /api/v3/meeting_agenda_items/{id}
|
||||
---
|
||||
get:
|
||||
summary: Get a meeting agenda item
|
||||
operationId: get_meeting_agenda_item
|
||||
tags:
|
||||
- Meetings
|
||||
description: Retrieve an individual agenda item of a meeting.
|
||||
description: Retrieve an individual agenda item.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Agenda item identifier
|
||||
example: 1
|
||||
in: path
|
||||
@@ -40,7 +33,7 @@ get:
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the agenda item or meeting does not exist or the client does not have sufficient permissions.
|
||||
Returned if the agenda item does not exist or the client does not have sufficient permissions.
|
||||
|
||||
patch:
|
||||
summary: Update a meeting agenda item
|
||||
@@ -49,13 +42,6 @@ patch:
|
||||
- Meetings
|
||||
description: Updates the given agenda item.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Agenda item identifier
|
||||
example: 1
|
||||
in: path
|
||||
@@ -82,12 +68,6 @@ patch:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
|
||||
message: You are not authorized to access this resource.
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
@@ -97,14 +77,8 @@ patch:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the agenda item or meeting does not exist.
|
||||
Returned if the agenda item does not exist or the client does not have sufficient permissions.
|
||||
'406':
|
||||
$ref: "../components/responses/missing_content_type.yml"
|
||||
'415':
|
||||
@@ -122,13 +96,6 @@ delete:
|
||||
- Meetings
|
||||
description: Deletes the agenda item.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Agenda item identifier
|
||||
example: 1
|
||||
in: path
|
||||
@@ -144,12 +111,6 @@ delete:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
|
||||
message: You are not authorized to access this resource.
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
@@ -159,11 +120,5 @@ delete:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the agenda item or meeting does not exist.
|
||||
Returned if the agenda item does not exist or the client does not have sufficient permissions.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# /api/v3/meetings/{meeting_id}/agenda_items/{id}
|
||||
---
|
||||
get:
|
||||
summary: Get a meeting agenda item
|
||||
operationId: get_meeting_agenda_item_by_meeting
|
||||
tags:
|
||||
- Meetings
|
||||
description: Retrieve an individual agenda item of a meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Agenda item identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_agenda_item_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the agenda item or meeting does not exist or the client does not have sufficient permissions.
|
||||
@@ -0,0 +1,50 @@
|
||||
# /api/v3/meetings/{meeting_id}/agenda_items/{agenda_item_id}/outcomes/{id}
|
||||
---
|
||||
get:
|
||||
summary: Get a meeting outcome
|
||||
operationId: get_meeting_outcome_by_agenda_item
|
||||
tags:
|
||||
- Meetings
|
||||
description: Retrieve an individual outcome of a meeting agenda item.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Agenda item identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: agenda_item_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Outcome identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the outcome, agenda item, or meeting does not exist or the client does not have sufficient permissions.
|
||||
@@ -0,0 +1,45 @@
|
||||
# /api/v3/meetings/{meeting_id}/agenda_items/{agenda_item_id}/outcomes
|
||||
---
|
||||
get:
|
||||
summary: List meeting outcomes
|
||||
operationId: list_meeting_outcomes
|
||||
tags:
|
||||
- Meetings
|
||||
description: Lists all outcomes for the given meeting agenda item.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Agenda item identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: agenda_item_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_collection_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the agenda item or meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
|
||||
**Required permission:** view meetings
|
||||
@@ -1,56 +1,11 @@
|
||||
# /api/v3/meetings/{id}/agenda_items
|
||||
# /api/v3/meeting_agenda_items
|
||||
---
|
||||
get:
|
||||
summary: List meeting agenda items
|
||||
operationId: list_meeting_agenda_items
|
||||
tags:
|
||||
- Meetings
|
||||
description: Lists all agenda items for the given meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_agenda_item_collection_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
|
||||
**Required permission:** view meetings
|
||||
|
||||
post:
|
||||
summary: Create meeting agenda item
|
||||
operationId: create_meeting_agenda_item
|
||||
tags:
|
||||
- Meetings
|
||||
description: Creates a new agenda item for the given meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: Creates a new agenda item. The request body must link the meeting.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@@ -92,7 +47,7 @@ post:
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
Returned if the linked meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
'406':
|
||||
$ref: "../components/responses/missing_content_type.yml"
|
||||
'415':
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# /api/v3/meetings/{id}/agenda_items
|
||||
---
|
||||
get:
|
||||
summary: List meeting agenda items
|
||||
operationId: list_meeting_agenda_items
|
||||
tags:
|
||||
- Meetings
|
||||
description: Lists all agenda items for the given meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_agenda_item_collection_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
|
||||
**Required permission:** view meetings
|
||||
@@ -0,0 +1,124 @@
|
||||
# /api/v3/meeting_outcomes/{id}
|
||||
---
|
||||
get:
|
||||
summary: Get a meeting outcome
|
||||
operationId: get_meeting_outcome
|
||||
tags:
|
||||
- Meetings
|
||||
description: Retrieve an individual meeting outcome.
|
||||
parameters:
|
||||
- description: Outcome identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the outcome does not exist or the client does not have sufficient permissions.
|
||||
|
||||
patch:
|
||||
summary: Update a meeting outcome
|
||||
operationId: update_meeting_outcome
|
||||
tags:
|
||||
- Meetings
|
||||
description: Updates the given meeting outcome.
|
||||
parameters:
|
||||
- description: Outcome identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_write_model.yml"
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_model.yml"
|
||||
'400':
|
||||
$ref: "../components/responses/invalid_request_body.yml"
|
||||
'403':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
**Required permission:** manage outcomes
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
description: |-
|
||||
Returned if the outcome does not exist or the client does not have sufficient permissions.
|
||||
'406':
|
||||
$ref: "../components/responses/missing_content_type.yml"
|
||||
'415':
|
||||
$ref: "../components/responses/unsupported_media_type.yml"
|
||||
'422':
|
||||
description: |-
|
||||
Returned if:
|
||||
|
||||
* a constraint for a property was violated (`PropertyConstraintViolation`)
|
||||
|
||||
delete:
|
||||
summary: Delete a meeting outcome
|
||||
operationId: delete_meeting_outcome
|
||||
tags:
|
||||
- Meetings
|
||||
description: Deletes the outcome.
|
||||
parameters:
|
||||
- description: Outcome identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'204':
|
||||
description: Returned if the outcome was successfully deleted
|
||||
'403':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
**Required permission:** manage outcomes
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
description: |-
|
||||
Returned if the outcome does not exist or the client does not have sufficient permissions.
|
||||
@@ -0,0 +1,59 @@
|
||||
# /api/v3/meeting_outcomes
|
||||
---
|
||||
post:
|
||||
summary: Create meeting outcome
|
||||
operationId: create_meeting_outcome
|
||||
tags:
|
||||
- Meetings
|
||||
description: Creates a new meeting outcome. The request body must link the agenda item.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_write_model.yml"
|
||||
responses:
|
||||
'201':
|
||||
description: Created
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_outcome_model.yml"
|
||||
'400':
|
||||
$ref: "../components/responses/invalid_request_body.yml"
|
||||
'403':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
|
||||
message: You are not authorized to access this resource.
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
**Required permission:** manage outcomes
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the linked agenda item does not exist or the client does not have sufficient permissions to see it.
|
||||
'406':
|
||||
$ref: "../components/responses/missing_content_type.yml"
|
||||
'415':
|
||||
$ref: "../components/responses/unsupported_media_type.yml"
|
||||
'422':
|
||||
description: |-
|
||||
Returned if:
|
||||
|
||||
* a constraint for a property was violated (`PropertyConstraintViolation`)
|
||||
@@ -1,19 +1,12 @@
|
||||
# /api/v3/meetings/{meeting_id}/sections/{id}
|
||||
# /api/v3/meeting_sections/{id}
|
||||
---
|
||||
get:
|
||||
summary: Get a meeting section
|
||||
operationId: get_meeting_section
|
||||
tags:
|
||||
- Meetings
|
||||
description: Retrieve an individual section of a meeting.
|
||||
description: Retrieve an individual meeting section.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Section identifier
|
||||
example: 1
|
||||
in: path
|
||||
@@ -33,29 +26,16 @@ get:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the section or meeting does not exist or the client does not have sufficient permissions.
|
||||
Returned if the section does not exist or the client does not have sufficient permissions.
|
||||
|
||||
patch:
|
||||
summary: Update a meeting section
|
||||
operationId: update_meeting_section
|
||||
tags:
|
||||
- Meetings
|
||||
description: Updates the given section.
|
||||
description: Updates the given meeting section.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Section identifier
|
||||
example: 1
|
||||
in: path
|
||||
@@ -82,12 +62,6 @@ patch:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
|
||||
message: You are not authorized to access this resource.
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
@@ -97,14 +71,8 @@ patch:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the section or meeting does not exist.
|
||||
Returned if the section does not exist or the client does not have sufficient permissions.
|
||||
'406':
|
||||
$ref: "../components/responses/missing_content_type.yml"
|
||||
'415':
|
||||
@@ -120,15 +88,8 @@ delete:
|
||||
operationId: delete_meeting_section
|
||||
tags:
|
||||
- Meetings
|
||||
description: Deletes the section and all its agenda items.
|
||||
description: Deletes the meeting section.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Section identifier
|
||||
example: 1
|
||||
in: path
|
||||
@@ -144,12 +105,6 @@ delete:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
|
||||
message: You are not authorized to access this resource.
|
||||
description: |-
|
||||
Returned if the client does not have sufficient permissions.
|
||||
|
||||
@@ -159,11 +114,5 @@ delete:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the section or meeting does not exist.
|
||||
Returned if the section does not exist or the client does not have sufficient permissions.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# /api/v3/meetings/{meeting_id}/sections/{id}
|
||||
---
|
||||
get:
|
||||
summary: Get a meeting section
|
||||
operationId: get_meeting_section_by_meeting
|
||||
tags:
|
||||
- Meetings
|
||||
description: Retrieve an individual section of a meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: meeting_id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- description: Section identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_section_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the section or meeting does not exist or the client does not have sufficient permissions.
|
||||
@@ -1,56 +1,11 @@
|
||||
# /api/v3/meetings/{id}/sections
|
||||
# /api/v3/meeting_sections
|
||||
---
|
||||
get:
|
||||
summary: List meeting sections
|
||||
operationId: list_meeting_sections
|
||||
tags:
|
||||
- Meetings
|
||||
description: Lists all sections for the given meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_section_collection_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
|
||||
**Required permission:** view meetings
|
||||
|
||||
post:
|
||||
summary: Create meeting section
|
||||
operationId: create_meeting_section
|
||||
tags:
|
||||
- Meetings
|
||||
description: Creates a new section for the given meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
description: Creates a new section. The request body must link the meeting.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@@ -92,7 +47,7 @@ post:
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
Returned if the linked meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
'406':
|
||||
$ref: "../components/responses/missing_content_type.yml"
|
||||
'415':
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# /api/v3/meetings/{id}/sections
|
||||
---
|
||||
get:
|
||||
summary: List meeting sections
|
||||
operationId: list_meeting_sections
|
||||
tags:
|
||||
- Meetings
|
||||
description: Lists all sections for the given meeting.
|
||||
parameters:
|
||||
- description: Meeting identifier
|
||||
example: 1
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/meeting_section_collection_model.yml"
|
||||
'404':
|
||||
content:
|
||||
application/hal+json:
|
||||
schema:
|
||||
$ref: "../components/schemas/error_response.yml"
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
_type: Error
|
||||
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
|
||||
message: The requested resource could not be found.
|
||||
description: |-
|
||||
Returned if the meeting does not exist or the client does not have sufficient permissions to see it.
|
||||
|
||||
**Required permission:** view meetings
|
||||
@@ -652,13 +652,61 @@ OPENPROJECT_HIDDEN__MENU__ITEMS_ADMIN__MENU="roles types statuses workflows enum
|
||||
|
||||
#### Rate limiting
|
||||
|
||||
OpenProject provides some rate limiting protections. The default configuration protects against repeated access to authentication credential resets (e.g., lost password functionality).
|
||||
OpenProject includes HTTP-layer rate limiting via Rack::Attack. The rules below are configured through the `rate_limiting` setting and take effect without a restart when set via environment variable.
|
||||
|
||||
You can optionally enable additional rules on API rate limiting as follows:
|
||||
In addition to these application-level rules, consider applying rate limiting at your load balancer or reverse proxy (e.g. `ngx_http_limit_req_module`, `mod_security`) for IP-level protection.
|
||||
|
||||
`OPENPROJECT_RATE_LIMITING_API__V3=true`
|
||||
##### Login brute-force protection (enabled by default)
|
||||
|
||||
Additional application-level rate limiting rules will be added in the future. Additionally to these application level rules, use your load balancer / proxying web server to apply individual rate limiting rules using modules such as `ngx_http_limit_req_module` or `mod_security`.
|
||||
OpenProject blocks repeated login attempts per account at the HTTP layer. After **20 POST `/login` requests for the same username within one minute**, that account is blocked for **30 minutes**. This works alongside the application-level lockout (`brute_force_block_after_failed_logins` / `brute_force_block_minutes` settings).
|
||||
|
||||
This rule is **enabled by default**. To disable it:
|
||||
|
||||
```shell
|
||||
OPENPROJECT_RATE_LIMITING_LOGIN="false"
|
||||
```
|
||||
|
||||
The thresholds can be tuned independently:
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `OPENPROJECT_RATE_LIMITING_LOGIN_BURST__LIMIT` | `20` | Number of attempts allowed before the ban is triggered |
|
||||
| `OPENPROJECT_RATE_LIMITING_LOGIN_BURST__PERIOD` | `60` | Detection window in seconds |
|
||||
| `OPENPROJECT_RATE_LIMITING_LOGIN_BAN__PERIOD` | `1800` | Ban duration in seconds |
|
||||
|
||||
Example: Stricter limits (10 attempts per minute, 1-hour ban):
|
||||
|
||||
```shell
|
||||
OPENPROJECT_RATE_LIMITING_LOGIN_BURST__LIMIT="10"
|
||||
OPENPROJECT_RATE_LIMITING_LOGIN_BURST__PERIOD="60"
|
||||
OPENPROJECT_RATE_LIMITING_LOGIN_BAN__PERIOD="3600"
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This rule and the application-level brute-force protection (`brute_force_block_after_failed_logins` /
|
||||
> `brute_force_block_minutes`) are independent controls that operate at different layers. The HTTP-layer
|
||||
> rule counts **all** login attempts (including successful ones) within its burst window, while the
|
||||
> application-level setting counts only **failed** attempts and operates over a longer rolling window.
|
||||
> If you lower `brute_force_block_after_failed_logins` below `BURST_LIMIT` (default 20), the
|
||||
> application-level lockout will fire before this rule does. Keep the two thresholds consistent to
|
||||
> avoid surprising behaviour. For example, set `BURST_LIMIT` to match or be lower than
|
||||
> `brute_force_block_after_failed_logins`.
|
||||
|
||||
##### Lost password rate limiting (disabled by default)
|
||||
|
||||
Limits password-reset requests per email address to 3 per hour.
|
||||
|
||||
```shell
|
||||
OPENPROJECT_RATE_LIMITING_LOST__PASSWORD="true"
|
||||
```
|
||||
|
||||
##### API v3 rate limiting (disabled by default)
|
||||
|
||||
Limits API form endpoint requests per session to 6 per 3 seconds.
|
||||
|
||||
```shell
|
||||
OPENPROJECT_RATE_LIMITING_API__V3="true"
|
||||
```
|
||||
|
||||
#### Blacklisted routes
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: OpenProject 17.6.0
|
||||
sidebar_navigation:
|
||||
title: 17.6.0
|
||||
release_version: 17.6.0
|
||||
release_date: 2026-06-08
|
||||
---
|
||||
|
||||
# OpenProject 17.6.0
|
||||
|
||||
Release date: 2026-06-08
|
||||
|
||||
We released [OpenProject 17.6.0](https://community.openproject.org/versions/2298).
|
||||
The release contains several bug fixes and we recommend updating to the newest version.
|
||||
In these Release Notes, we will give an overview of important feature changes. At the end, you will find a complete list of all changes and bug fixes.
|
||||
## Important feature changes
|
||||
|
||||
<!-- Inform about the major features in this section -->
|
||||
|
||||
## Important updates and breaking changes
|
||||
|
||||
### Integrations (e.g. Nextcloud and XWiki) respect global SSRF filters
|
||||
|
||||
To increase the security of OpenProject installations, we've added protections against server-side request forgery in previous releases
|
||||
of OpenProject. These prevent OpenProject from making network requests into private IP address space.
|
||||
|
||||
Starting with OpenProject 17.6, these protections expand into the code that's responsible for web requests of storage and wiki integrations as well.
|
||||
This means if you have a Nextcloud instance or an XWiki instance reachable via a private (i.e. not publicly routable) IP address, you need to
|
||||
add it to the SSRF allowlist to be able to keep the integration working. This is usually achieved by defining the following environment variable:
|
||||
|
||||
```
|
||||
OPENPROJECT_SSRF_PROTECTION_IP_ALLOWLIST=2001:db8:100::/48
|
||||
```
|
||||
|
||||
The list accepts one or multiple IP addresses or ranges (in CIDR notation) that shall be exempt from SSRF filtering.
|
||||
|
||||
### Meeting API structure changes
|
||||
|
||||
17.6. introduces new endpoints for meeting outcomes,
|
||||
and changes the self link for all meeting related resources to be flat:
|
||||
|
||||
That means, some of the responses have changed:
|
||||
|
||||
POST/PATCH/DELETE `/api/v3/meetings/:id/agenda_items)` is no longer available,
|
||||
they have been moved to the `/api/v3/meeting_agendas/` respectively. The same is true for outcomes and sections.
|
||||
|
||||
This follows the APIv3 standards, and also fixes a bug related to the self link.
|
||||
|
||||
<!-- BEGIN SECURITY FIXES AUTOMATED SECTION -->
|
||||
|
||||
<!-- END SECURITY FIXES AUTOMATED SECTION -->
|
||||
<!--more-->
|
||||
|
||||
## Bug fixes and changes
|
||||
|
||||
<!-- Warning: Anything within the below lines will be automatically removed by the release script -->
|
||||
<!-- BEGIN AUTOMATED SECTION -->
|
||||
|
||||
- Feature: Sprint goals \[[#71059](https://community.openproject.org/wp/71059)\]
|
||||
- Feature: Add possibility to order backlog buckets and sprints manually \[[#73610](https://community.openproject.org/wp/73610)\]
|
||||
- Feature: Add multi-select drop-down for sprint and backlog buckets \[[#73611](https://community.openproject.org/wp/73611)\]
|
||||
- Feature: Multi-select cards within backlog and sprints \[[#73729](https://community.openproject.org/wp/73729)\]
|
||||
- Feature: Display backlog bucket in work package page \[[#73887](https://community.openproject.org/wp/73887)\]
|
||||
- Feature: "Move to backlog bucket" and "move to backlog inbox" menu option for work packages within the backlog module \[[#73925](https://community.openproject.org/wp/73925)\]
|
||||
- Feature: Add existing work packages within sprint and backlog containers menu \[[#74386](https://community.openproject.org/wp/74386)\]
|
||||
- Feature: "All sprints" view - simple list \[[#74594](https://community.openproject.org/wp/74594)\]
|
||||
- Feature: Column, ordering and grouping by backlog bucket in work package list \[[#74653](https://community.openproject.org/wp/74653)\]
|
||||
- Feature: Show message when work package with excluded type/status is moved to backlog and disappears \[[#74845](https://community.openproject.org/wp/74845)\]
|
||||
- Feature: Check the accessibility on Flash messages \[[#63276](https://community.openproject.org/wp/63276)\]
|
||||
- Feature: Remove newest projects in project widget on homepage \[[#74198](https://community.openproject.org/wp/74198)\]
|
||||
- Feature: Make project hierarchy collapsable in the global project selector \[[#74625](https://community.openproject.org/wp/74625)\]
|
||||
- Feature: Create work package out of Meeting Agenda Item \[[#57053](https://community.openproject.org/wp/57053)\]
|
||||
- Feature: API for Meeting outcomes \[[#75393](https://community.openproject.org/wp/75393)\]
|
||||
- Feature: Group synchronization through attributes of the group, not member/memberOf \[[#32812](https://community.openproject.org/wp/32812)\]
|
||||
- Feature: Track working hours and availabilities for each user in the system \[[#34911](https://community.openproject.org/wp/34911)\]
|
||||
- Feature: Allow cost types to be enabled/disabled per project \[[#42037](https://community.openproject.org/wp/42037)\]
|
||||
- Feature: All open view with default sort order to show the latest on top (ID descending) \[[#57962](https://community.openproject.org/wp/57962)\]
|
||||
- Feature: New Administration for User Custom Fields and Custom Field Sections \[[#72005](https://community.openproject.org/wp/72005)\]
|
||||
- Feature: Primerize advanced filters component \[[#74380](https://community.openproject.org/wp/74380)\]
|
||||
- Feature: Build Primer quickfilter \[[#74577](https://community.openproject.org/wp/74577)\]
|
||||
- Feature: Enforce order of subheader slots/quickfilters \[[#75013](https://community.openproject.org/wp/75013)\]
|
||||
- Feature: Escape possible control characters in CSV export \[[#75486](https://community.openproject.org/wp/75486)\]
|
||||
- Feature: Filter project by portfolio and programm \[[#74718](https://community.openproject.org/wp/74718)\]
|
||||
- Feature: Adapt Excel and CSV exports for semantic identifiers \[[#74361](https://community.openproject.org/wp/74361)\]
|
||||
- Feature: Adapt BCF Export and Import for semantic identifiers \[[#74362](https://community.openproject.org/wp/74362)\]
|
||||
- Feature: Adapt other PDF exports for semantic identifiers \[[#75229](https://community.openproject.org/wp/75229)\]
|
||||
- Feature: /wp on an empty line should create a block work-package link, not an inline one \[[#75310](https://community.openproject.org/wp/75310)\]
|
||||
- Feature: Expose installation UUID via API \[[#75442](https://community.openproject.org/wp/75442)\]
|
||||
- Feature: Configure internal wiki provider \[[#75594](https://community.openproject.org/wp/75594)\]
|
||||
- Feature: Show total sprint capacity (in days or story points) \[[#71060](https://community.openproject.org/wp/71060)\]
|
||||
- Feature: "All Sprints" view \[[#71260](https://community.openproject.org/wp/71260)\]
|
||||
- Feature: Allow multiple active sprints within a single project \[[#73232](https://community.openproject.org/wp/73232)\]
|
||||
- Feature: XWiki integration \[[#53738](https://community.openproject.org/wp/53738)\]
|
||||
- Feature: Extend CKEditor with Wiki interactions and macros \[[#70554](https://community.openproject.org/wp/70554)\]
|
||||
- Feature: Wiki tab in work package detail view \[[#70555](https://community.openproject.org/wp/70555)\]
|
||||
- Feature: Wiki integration setup on OpenProject \[[#70556](https://community.openproject.org/wp/70556)\]
|
||||
- Bugfix: Page loads twice after sprint creation \[[#73316](https://community.openproject.org/wp/73316)\]
|
||||
- Bugfix: A missing full stop at the end of confirmation message of danger dialog \[[#73899](https://community.openproject.org/wp/73899)\]
|
||||
- Bugfix: Impossible to open work packages list from the sidebar after visiting team planner \[[#74331](https://community.openproject.org/wp/74331)\]
|
||||
- Bugfix: Input group with trailing action clipboard copy button + validation error = style broken \[[#75395](https://community.openproject.org/wp/75395)\]
|
||||
- Bugfix: FilterableTreeView does not keep default filter arguments \[[#75617](https://community.openproject.org/wp/75617)\]
|
||||
- Bugfix: Tree view selection based on path identity breaks use cases where similar paths are allowed \[[#75618](https://community.openproject.org/wp/75618)\]
|
||||
- Bugfix: Fix tracking expression browser warnings \[[#75676](https://community.openproject.org/wp/75676)\]
|
||||
- Bugfix: GET /api/v3/meetings/{id} — \_links.participants count does not match \_embedded.participants count \[[#75696](https://community.openproject.org/wp/75696)\]
|
||||
- Bugfix: PATCH /api/v3/meetings/{id} — adding an already existing participant via \_links.participants creates a duplicate entry \[[#75697](https://community.openproject.org/wp/75697)\]
|
||||
- Bugfix: PATCH /api/v3/meetings/{id} - participants cannot be removed via \_links.participants \[[#75701](https://community.openproject.org/wp/75701)\]
|
||||
- Bugfix: WP table configuration: overflow due to the very long CF label \[[#46005](https://community.openproject.org/wp/46005)\]
|
||||
- Bugfix: Tooltip on Team planner not entirely visible \[[#48223](https://community.openproject.org/wp/48223)\]
|
||||
- Bugfix: Problems with GitLab and GitHub integration snippets \[[#56847](https://community.openproject.org/wp/56847)\]
|
||||
- Bugfix: Misalignment of fields in Work estimates and progress when language=DE \[[#65738](https://community.openproject.org/wp/65738)\]
|
||||
- Bugfix: Custom text widget pagination bug \[[#66419](https://community.openproject.org/wp/66419)\]
|
||||
- Bugfix: Arrow for switching years barely visible in dark mode on the calendar \[[#68517](https://community.openproject.org/wp/68517)\]
|
||||
- Bugfix: Login right side panel dark mode: login form has ugly/unnecessary gray background \[[#69328](https://community.openproject.org/wp/69328)\]
|
||||
- Bugfix: User sees a success banner if they save a letter/word as integer \[[#71650](https://community.openproject.org/wp/71650)\]
|
||||
- Bugfix: Closed, duplicated meeting disappears from synced calendar \[[#72219](https://community.openproject.org/wp/72219)\]
|
||||
- Bugfix: Wrong icon used when changing non working days \[[#73372](https://community.openproject.org/wp/73372)\]
|
||||
- Bugfix: User facing work package link from GitLab tab is not the shortened version \[[#73718](https://community.openproject.org/wp/73718)\]
|
||||
- Bugfix: Inline text attachments lose UTF-8 charset \[[#75402](https://community.openproject.org/wp/75402)\]
|
||||
- Bugfix: BCF import permission scope not clear \[[#75457](https://community.openproject.org/wp/75457)\]
|
||||
- Bugfix: Hide "my meetings" and "favourited projects" widgets for anonymous users \[[#75477](https://community.openproject.org/wp/75477)\]
|
||||
- Bugfix: Setting mail header via OPENPROJECT\_EMAILS\_\_HEADER\_EN interprets colon as hash \[[#75570](https://community.openproject.org/wp/75570)\]
|
||||
- Bugfix: Date custom field filter "is empty" does not return all work packages with empty values \[[#75185](https://community.openproject.org/wp/75185)\]
|
||||
|
||||
<!-- END AUTOMATED SECTION -->
|
||||
<!-- Warning: Anything above this line will be automatically removed by the release script -->
|
||||
|
||||
## Contributions
|
||||
A very special thank you goes to our sponsors for this release.
|
||||
Also a big thanks to our Community members for reporting bugs and helping us identify and provide fixes.
|
||||
Special thanks for reporting and finding bugs go to Rince wind, Walid Ibrahim, Gábor Alexovics, Brandon Soonaye, Mohammed Mohiuddin.
|
||||
|
||||
Last but not least, we are very grateful for our very engaged translation contributors on Crowdin, who translated quite a few OpenProject strings!
|
||||
Would you like to help out with translations yourself?
|
||||
Then take a look at our translation guide and find out exactly how you can contribute.
|
||||
It is very much appreciated!
|
||||
@@ -52,7 +52,7 @@ For context: The connection of custom SSO providers is also described [here](../
|
||||
## I want to connect AD and LDAP to OpenProject. Which attribute for authentication sources does OpenProject use?
|
||||
|
||||
You can freely define the attributes that are taken from LDAP sources [in the LDAP auth source configuration screen](../ldap-connections/).
|
||||
For group synchronization, OpenProject supports the AD/LDAP standard for groups via "member / memberOf". The attribute cannot be configured at this time.
|
||||
For group synchronization, OpenProject defaults to reverse lookup via the `memberOf` attribute on user entries (Active Directory, OpenLDAP with memberof overlay). If your LDAP server does not maintain `memberOf` on user entries, you can configure forward lookup by setting the **Group member attribute** on a synchronized filter (e.g. `uniqueMember` for `groupOfUniqueNames`, or `member` for `groupOfNames`). See [LDAP group synchronization](../ldap-connections/ldap-group-synchronization/) for details.
|
||||
|
||||
## Is there an option to mass-create users in OpenProject via the LDAP?
|
||||
|
||||
|
||||
+29
-17
@@ -8,15 +8,15 @@ keywords: synchronize ldap groups
|
||||
|
||||
# Synchronize LDAP and OpenProject groups (Enterprise add-on)
|
||||
|
||||
Note: This feature is available for the Enterprise on-premises only, for OpenProject versions 7.4+. For more information and differences to Community edition, [see this page](https://www.openproject.org/enterprise-edition/).
|
||||
This feature is available for the Enterprise edition only. For more information and differences to Community edition, [see the Enterprise edition overview](https://www.openproject.org/enterprise-edition/).
|
||||
|
||||
In OpenProject Enterprise on-premises, you can provision and periodically synchronize groups and their members from your existing LDAP or Active Directory. Group memberships using the [groupOfNames](https://tools.ietf.org/html/rfc4519#section-3.5) LDAP object class are supported for this. This guide assumes that you:
|
||||
In OpenProject Enterprise edition, you can provision and periodically synchronize groups and their members from your existing LDAP or Active Directory. This guide assumes that you:
|
||||
|
||||
- have set up your LDAP authentication source (See the “[Manage LDAP authentication](../../ldap-connections/)” guide)
|
||||
- have at least one LDAP entry with a *groupOfNames* object class and members of that group to contain the *`memberOf: <DN of the group>`* attribute to determine the members of a group entry. Right now we do not support LDAP instances that only have *member* attributes, but not the inverse *memberOf* property.
|
||||
- have LDAP group entries whose members can be discovered either via the `memberOf` attribute on user entries (reverse lookup, default) or via a member list attribute on the group entry such as `uniqueMember` or `member` (forward lookup, configurable).
|
||||
|
||||
> [!NOTE]
|
||||
> OpenProject does not support other attributes other than the `memberOf` property to define groups. Please make sure that user objects have the `memberOf` property for the synchronization to work.
|
||||
> This feature is available for both Enterprise cloud and Enterprise on-premises. When using Enterprise cloud, your LDAP server must be reachable from the OpenProject cloud infrastructure, which typically means exposing it to the internet, which is not recommended. This is a network and security consideration for your organization to evaluate. Consider using [SAML](../../saml/) or [SCIM provisioning](../../scim/) as a more secure alternative.
|
||||
|
||||
For the sake of simplicity, we assume that in this guide, your LDAP structure looks like the following:
|
||||
|
||||
@@ -35,7 +35,7 @@ In order to get to the LDAP group sync administration pane, expand the LDAP auth
|
||||
|
||||
Synchronizing a single LDAP group allows you to connect an existing group in OpenProject with one from LDAP.
|
||||
|
||||
LDAP group synchronization extends the memberships defined by administrators in an existing OpenProject group. Important things to note are:
|
||||
LDAP group synchronization extends the memberships defined by administrators in an existing OpenProject group. Important things to note are:
|
||||
|
||||
- You need to have created at least one manual group in the OpenProject administration before you continue.
|
||||
- Group synchronization for this group is enabled by an administrator creating a *synchronized LDAP group* that ties the OpenProject group to an LDAP entry.
|
||||
@@ -43,30 +43,29 @@ LDAP group synchronization extends the memberships defined by administrators in
|
||||
|
||||
### Single synchronized groups
|
||||
|
||||
To create a new synchronized group, use the button on the top right of the page. There, you will select your LDAP authentication source that contains the group, as well as the existing OpenProject group that members should be synchronized to. The following options can be set:
|
||||
To create a new synchronized group, use the button on the top right of the page. There, you will select your LDAP authentication source that contains the group, as well as the existing OpenProject group that members should be synchronized to. The following options can be set:
|
||||
|
||||
- **LDAP connection:** Select the LDAP connection you want this synchronized group to use. Users created by group synchronization will be tied to that LDAP and may bind against it for authentication.
|
||||
- **DN:** Enter the full distinguished name (DN) of the group you want to synchronize. For example: `cn=team1,ou=groups,dc=example,dc=com`.
|
||||
- **Sync users:** Check this option if you want members of this group to be automatically created in OpenProject. When unchecked, only members of the group that also are existing users in OpenProject can be synchronized.
|
||||
- **Group:** Select an OpenProject group you want the members of the LDAP group to synchronize to.
|
||||
|
||||
Click on *Create* to finish the creation of the synchronized group. The LDAP memberships of each user will be synchronized hourly through a background job on your packaged installation. Changes and output will be logged to */var/log/openproject/cron-hourly.log*.
|
||||
Click on *Create* to finish the creation of the synchronized group. The LDAP memberships of each user will be synchronized hourly through a background job.
|
||||
|
||||
If you want to trigger the synchronization *manually* you can do so by running the respective rake task directly.
|
||||
In the packaged installation, for instance, this would work like this:
|
||||
To trigger a full synchronization manually (group discovery **and** member sync), run the following rake task in a [console](../../../../installation-and-operations/operation/control/):
|
||||
|
||||
```shell
|
||||
sudo openproject run bundle exec rake ldap_groups:synchronize
|
||||
bundle exec rake ldap_groups:synchronize
|
||||
```
|
||||
|
||||
This method of creating synchronized groups is well-suited for a small number of groups, or a very individual set of groups that you need to synchronize. It is very flexible by allowing individual groups to synchronize users into OpenProject.
|
||||
This method of creating synchronized groups is well-suited for a small number of groups, or a carefully selected set of groups that you need to synchronize. It is very flexible by allowing individual groups to synchronize users into OpenProject.
|
||||
|
||||
If you need to synchronize a large number of groups that follow a common pattern, consider using the following filter functionality.
|
||||
|
||||
|
||||
## Automatic groups using synchronized filters
|
||||
|
||||
Instead of manually synchronizing groups from a given group DN in your LDAP, you can also create filter objects that will query the LDAP not only for group members, but the groups themselves.
|
||||
Instead of manually synchronizing groups from a given group DN in your LDAP, you can also create filter objects that automatically discover matching groups in your LDAP and synchronize their members.
|
||||
|
||||
When the synchronization task is executed, the filter is being queried against the LDAP and resulting group objects will be created as synchronized groups *and* as OpenProject groups.
|
||||
|
||||
@@ -82,11 +81,23 @@ To create a new synchronized filter, use the button on the top right of the inde
|
||||
- **LDAP connection:** Select the LDAP connection you want this synchronized filter to use. Users created by group synchronization will be tied to that LDAP and may bind against it for authentication.
|
||||
- **Search base DN:** (optional) Enter the base DN of the LDAP subtree you want to perform the search in. If you leave this unset, the base DN of the LDAP connection will be used instead. The DN specified here must contain the base DN of the LDAP connection to be valid.
|
||||
- **LDAP filter:** The LDAP filter string to be used for identifying LDAP group entries to be synchronized with OpenProject.
|
||||
- **Group member attribute:** (optional) The attribute on group entries that lists member DNs, used for forward lookup. Leave empty (default) to use reverse lookup via the `memberOf` attribute on user entries. Set this to `uniqueMember` for `groupOfUniqueNames` schemas, or `member` for `groupOfNames` schemas. Use forward lookup when your LDAP server does not maintain `memberOf` on user entries.
|
||||
|
||||
Click on *Create* to finish the creation of the synchronized filter. This filter is being executed hourly as part of the background job before the actual group synchronization runs.
|
||||
Click on *Create* to finish the creation of the synchronized filter. This filter is being executed hourly as part of the background job before the actual group synchronization runs.
|
||||
|
||||
Once the filter is created, the **Discover LDAP groups** button on the filter detail page runs the discovery immediately: it queries LDAP using the filter and creates the corresponding synchronized groups and OpenProject groups. It does **not** synchronize group members — member synchronization is handled by the hourly background job.
|
||||
|
||||
To trigger a full synchronization (group discovery **and** member sync) manually, run the following rake task in a [console](../../../../installation-and-operations/operation/control/):
|
||||
|
||||
```shell
|
||||
bundle exec rake ldap_groups:synchronize
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you manually create a synchronized group that is also found by a filter, its properties (such as the *Sync users* setting) is being overridden by the filter setting.
|
||||
> Forward lookup via the **Group member attribute** is only available for groups created through a synchronized filter. Manually-created synchronized groups always use reverse lookup (`memberOf`).
|
||||
|
||||
> [!NOTE]
|
||||
> If you manually create a synchronized group that is also found by a filter, its properties (such as the *Sync users* setting) are overridden by the filter settings.
|
||||
|
||||
## FAQ
|
||||
|
||||
@@ -108,6 +119,7 @@ Please double check the DN of the groups and the LDAP connection. The base DN of
|
||||
For users to be automatically synchronized, the following conditions need to be met:
|
||||
|
||||
1. The connection, or the LDAP group need to have "Sync users" checked. This setting overrides the LDAP connection's "Sync users" attribute for fine-grained control over which groups will have users synchronized.
|
||||
2. The group needs to define their members using the `member` LDAP property, and users need to have the `memberOf` property or virtual property. OpenProject will look for users with the following filter: `(memberOf=<DN of the group>).` You can use `ldapsearch` to verify that this works as expected.
|
||||
3. The users defined in the groups need to have all required attributes present to be created. These are at *login, email, first and last name*. If any of these attributes are missing, the user cannot be saved to the database.
|
||||
4. If you enterprise license exceeds the user limit, new users can also not be synchronized through LDAP. A corresponding log entry will be logged.
|
||||
2. OpenProject needs to be able to resolve group members. By default it uses **reverse lookup**: it searches for users with the filter `(memberOf=<DN of the group>)`, which requires `memberOf` to be present on user entries (Active Directory, OpenLDAP with memberof overlay). If your LDAP server does not support `memberOf`, configure **forward lookup** by setting the **Group member attribute** on the synchronized filter (e.g. `uniqueMember` or `member`). You can use `ldapsearch` to verify either approach works as expected.
|
||||
3. The users defined in the groups need to have all required attributes present and non-empty to be created: *login, email, first and last name*. If any of these attributes are missing or empty, the user cannot be saved to the database.
|
||||
4. The **Login** attribute configured on the LDAP connection must match an attribute that is actually present on your LDAP user entries. If the attribute name is wrong or absent, users will be found in the group but not matched or created in OpenProject. Verify the attribute name against your LDAP schema and update the LDAP connection configuration if needed.
|
||||
5. If your enterprise license exceeds the user limit, new users cannot be synchronized through LDAP. On-premises users will find a corresponding entry in the application logs.
|
||||
|
||||
Reference in New Issue
Block a user