Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release/14.2' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverguenther committed Jun 24, 2024
2 parents 0be6a4c + ab7c008 commit db7a65e
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 53 deletions.
3 changes: 2 additions & 1 deletion config/initializers/permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@
{},
permissible_on: :project,
require: :loggedin,
dependencies: :view_work_packages
dependencies: :view_work_packages,
contract_actions: { queries: %i[create] }
# Watchers
wpt.permission :view_work_package_watchers,
{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ docker run -d -p 8080:80 --name openproject \
```

Please make sure you set the correct public facing hostname in `OPENPROJECT_HOST__NAME`. If you don't have a load-balancing or proxying web server in front of your docker container,
you will otherwise be vulnerable to [HOST header injections](https://portswigger.net/web-security/host-header), as the internal server has no way of identifying the correct host name.
you will otherwise be vulnerable to [HOST header injections](https://portswigger.net/web-security/host-header), as the internal server has no way of identifying the correct host name. We strongly recommend you use an external load-balancing or proxying web server for termination of TLS/SSL and general security hardening.

**Note**: Make sure to replace `secret` with a random string. One way to generate one is to run `head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32 ; echo ''` if you are on Linux.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<ng-container class="board-list--more-menu" *ngIf="canDelete()">
<icon-triggered-context-menu
[menu-items]="menuItems">
[menuItemsFactory]="menuItems">
</icon-triggered-context-menu>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ import { GridAreaService } from 'core-app/shared/components/grids/grid/area.serv
export abstract class WidgetAbstractMenuComponent {
@Input() resource:GridWidgetResource;

protected menuItemList:OpContextMenuItem[] = [this.removeItem];

constructor(readonly injector:Injector,
readonly i18n:I18nService,
protected readonly remove:GridRemoveWidgetService,
protected readonly layout:GridAreaService) {
}

public get menuItems() {
return async () => this.menuItemList;
public get menuItemsFactory():() => Promise<OpContextMenuItem[]> {
return this.buildItems.bind(this);
}

protected async buildItems():Promise<OpContextMenuItem[]> {
return [this.removeItem];
}

protected get removeItem():OpContextMenuItem {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<icon-triggered-context-menu
*ngIf="hasMenu"
[menu-items]="menuItems">
[menuItemsFactory]="menuItemsFactory">
</icon-triggered-context-menu>
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,41 @@
// See COPYRIGHT and LICENSE files for more details.
//++

import {
Directive, EventEmitter, Output,
} from '@angular/core';
import { Directive, EventEmitter, Output } from '@angular/core';
import { OpModalService } from 'core-app/shared/components/modal/modal.service';
import { ComponentType } from '@angular/cdk/portal';
import { WidgetAbstractMenuComponent } from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component';
import { WpGraphConfigurationModalComponent } from 'core-app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal';
import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal';
import {
WidgetAbstractMenuComponent,
} from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component';
import {
WpGraphConfigurationModalComponent,
} from 'core-app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal';
import {
WpTableConfigurationModalComponent,
} from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types';

@Directive()
export abstract class WidgetWpSetMenuComponent extends WidgetAbstractMenuComponent {
protected configurationComponent:ComponentType<WpGraphConfigurationModalComponent | WpTableConfigurationModalComponent>;

@InjectField() opModalService:OpModalService;

@Output() onConfigured:EventEmitter<any> = new EventEmitter();
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
@Output() onConfigured:EventEmitter<unknown> = new EventEmitter();

protected async buildItems():Promise<OpContextMenuItem[]> {
const items = [
this.removeItem,
];

protected menuItemList = [
this.removeItem,
this.configureItem,
];
if (await this.configurationAllowed()) {
items.push(this.configureItem);
}

return items;
}

protected get configureItem() {
return {
Expand All @@ -65,6 +78,10 @@ export abstract class WidgetWpSetMenuComponent extends WidgetAbstractMenuCompone
};
}

protected configurationAllowed():Promise<boolean> {
return Promise.resolve(true);
}

protected get locals() {
return {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import {
Component,
OnInit,
} from '@angular/core';
import { WidgetAbstractMenuComponent } from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component';
import {
WidgetAbstractMenuComponent,
} from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component';
import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
Expand Down Expand Up @@ -60,16 +62,14 @@ export class WidgetProjectDetailsMenuComponent extends WidgetAbstractMenuCompone
);
}

public get menuItems() {
return async () => {
const items = [
this.removeItem,
];
if (await this.capabilityPromise) {
items.push(this.projectActivityLinkItem);
}
return items;
};
protected async buildItems():Promise<OpContextMenuItem[]> {
const items = [
this.removeItem,
];
if (await this.capabilityPromise) {
items.push(this.projectActivityLinkItem);
}
return items;
}

protected get projectActivityLinkItem():OpContextMenuItem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ import {
Component, EventEmitter, Output,
} from '@angular/core';
import { OpModalService } from 'core-app/shared/components/modal/modal.service';
import { WidgetAbstractMenuComponent } from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component';
import { TimeEntriesCurrentUserConfigurationModalComponent } from 'core-app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal';
import {
WidgetAbstractMenuComponent,
} from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component';
import {
TimeEntriesCurrentUserConfigurationModalComponent,
} from 'core-app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types';

@Component({
selector: 'widget-time-entries-current-user-menu',
Expand All @@ -43,10 +48,12 @@ export class WidgetTimeEntriesCurrentUserMenuComponent extends WidgetAbstractMen

@Output() onConfigured:EventEmitter<any> = new EventEmitter();

protected menuItemList = [
this.removeItem,
this.configureItem,
];
protected async buildItems():Promise<OpContextMenuItem[]> {
return [
this.removeItem,
this.configureItem,
];
}

protected get configureItem() {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,26 @@
//++

import { Component } from '@angular/core';
import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal';
import {
WpTableConfigurationModalComponent,
} from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal';
import { WidgetWpSetMenuComponent } from 'core-app/shared/components/grids/widgets/menu/wp-set-menu.component';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { CurrentUserService } from 'core-app/core/current-user/current-user.service';
import { firstValueFrom } from 'rxjs';

@Component({
selector: 'widget-wp-table-menu',
templateUrl: '../menu/widget-menu.component.html',
})
export class WidgetWpTableMenuComponent extends WidgetWpSetMenuComponent {
@InjectField() currentUser:CurrentUserService;

protected configurationComponent = WpTableConfigurationModalComponent;

protected configurationAllowed():Promise<boolean> {
return firstValueFrom(
this.currentUser.hasCapabilities$('queries/create', null),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
// See COPYRIGHT and LICENSE files for more details.
//++

import {
ChangeDetectorRef, Component, ElementRef, Injector, Input,
} from '@angular/core';
import { ChangeDetectorRef, Component, ElementRef, Injector, Input } from '@angular/core';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive';
import {
OpContextMenuTrigger,
} from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive';
import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service';
import { OpModalService } from 'core-app/shared/components/modal/modal.service';
import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types';
Expand All @@ -41,16 +41,18 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op
styleUrls: ['./icon-triggered-context-menu.component.sass'],
})
export class IconTriggeredContextMenuComponent extends OpContextMenuTrigger {
constructor(readonly elementRef:ElementRef,
constructor(
readonly elementRef:ElementRef,
readonly opContextMenu:OPContextMenuService,
readonly opModalService:OpModalService,
readonly injector:Injector,
readonly cdRef:ChangeDetectorRef,
readonly I18n:I18nService) {
readonly I18n:I18nService,
) {
super(elementRef, opContextMenu);
}

@Input('menu-items') menuItems:Function;
@Input() menuItemsFactory:() => Promise<OpContextMenuItem[]>;

protected async open(evt:JQuery.TriggeredEvent) {
this.items = await this.buildItems();
Expand Down Expand Up @@ -78,8 +80,8 @@ export class IconTriggeredContextMenuComponent extends OpContextMenuTrigger {
const items:OpContextMenuItem[] = [];

// Add action specific menu entries
if (this.menuItems) {
const additional = await this.menuItems();
if (this.menuItemsFactory) {
const additional = await this.menuItemsFactory();
return items.concat(additional);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,11 @@

meetings_tab.open_add_to_meeting_dialog

click_on("Save")
retry_block do
click_on("Save")

expect(page).to have_content("Meeting can't be blank")
expect(page).to have_content("Meeting can't be blank")
end
end

it "adds presenter when the work package is added to a meeting" do
Expand Down
16 changes: 12 additions & 4 deletions modules/my_page/lib/my_page/grid_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@ class GridRegistration < ::Grids::Configuration::Registration
options_representer "::API::V3::Grids::Widgets::QueryOptionsRepresenter"
end

# Allow users without save_queries permission to access the widgets
# but they are not allowed to update the underlying query
wp_static_table_strategy_proc = Proc.new do
after_destroy -> { ::Query.find_by(id: options[:queryId])&.destroy }

options_representer "::API::V3::Grids::Widgets::QueryOptionsRepresenter"
end

widget_strategy "work_packages_table", &wp_table_strategy_proc
widget_strategy "work_packages_assigned", &wp_table_strategy_proc
widget_strategy "work_packages_accountable", &wp_table_strategy_proc
widget_strategy "work_packages_watched", &wp_table_strategy_proc
widget_strategy "work_packages_created", &wp_table_strategy_proc
widget_strategy "work_packages_assigned", &wp_static_table_strategy_proc
widget_strategy "work_packages_accountable", &wp_static_table_strategy_proc
widget_strategy "work_packages_watched", &wp_static_table_strategy_proc
widget_strategy "work_packages_created", &wp_static_table_strategy_proc

widget_strategy "time_entries_current_user" do
options_representer "::API::V3::Grids::Widgets::TimeEntryCalendarOptionsRepresenter"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require "spec_helper"

require_relative "../../support/pages/my/page"

RSpec.describe "Work package watched widget on My page", :js do
shared_let(:user) { create(:user) }
shared_let(:non_member) { create(:non_member, permissions: [:view_work_packages]) }
shared_let(:project) { create(:project, public: true) }
shared_let(:work_package) do
create(:work_package,
project:,
subject: "Visible work package for non member",
author: user,
responsible: user)
end

let(:my_page) do
Pages::My::Page.new
end

before do
login_as user
work_package.add_watcher(user)

my_page.visit!
end

it "can add the watcher widget without being member anywhere (Regression #55838)" do
my_page.add_widget(1, 1, :within, "Work packages watched by me")

expect(page).to have_text(work_package.subject)
end
end
5 changes: 4 additions & 1 deletion modules/overviews/lib/overviews/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ class Engine < ::Rails::Engine

OpenProject::AccessControl.permission(:view_work_packages)
.controller_actions
.push("overviews/overviews/show")
.push(
"overviews/overviews/show",
"overviews/overviews/project_custom_fields_sidebar"
)

OpenProject::AccessControl.map do |ac_map|
ac_map.project_module nil do |map|
Expand Down
Loading

0 comments on commit db7a65e

Please sign in to comment.