Skip to content

Commit 700bdfb

Browse files
committed
Merge branch 'dev' for release 6.5.4
2 parents 54393b3 + 2f974e2 commit 700bdfb

18 files changed

Lines changed: 411 additions & 256 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## Next release
44

5+
## v6.5.4 2026 March 31
6+
7+
- Fix a security issue: restrict exposed personal data in `/api/last_subscribed`, `/api/members` and `/api/members/:id` for public and non-privileged users
8+
9+
## v6.5.3 2026 March 26
10+
11+
- improvement: update memeber link to edit instead of show in reservations
12+
513
## v6.5.2 2026 March 4
614

715
- Fix a bug: unable to update event slots when recurrent event is modified

Gemfile.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ PLATFORMS
551551
x86_64-darwin-20
552552
x86_64-darwin-21
553553
x86_64-darwin-23
554+
x86_64-darwin-25
554555
x86_64-linux
555556

556557
DEPENDENCIES

app/controllers/api/members_controller.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@ def index
1616
# remove unmerged profiles from list
1717
@members = @query.to_a
1818
@members.delete_if(&:need_completion?)
19+
@restricted_member_index = !current_user.privileged?
1920
end
2021

2122
def last_subscribed
22-
@query, @members = Members::MembersService.last_registered(params[:last])
23-
24-
@requested_attributes = ['profile']
23+
@query, @members = Members::MembersService.last_registered
24+
@public_last_subscribed = true
2525
render :index
2626
end
2727

2828
def show
2929
@member = User.friendly.find(params[:id])
3030
authorize @member
31+
@restricted_member_show = !current_user.privileged?
3132
end
3233

3334
def create

app/frontend/src/javascript/lib/format.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export default class FormatLib {
6262
*/
6363
private static parseISOtime = (date: TDateISO|TDateISOShortTime): Date => {
6464
const isoTimeMatch = (date as string)?.match(/(^|T)(\d\d:\d\d)/);
65-
return new Date(`${moment().format('YYYY-MM-DD')}T${isoTimeMatch[2]}:00${Fablab.timezone_offset}`);
65+
return moment.tz(`${moment().format('YYYY-MM-DD')}T${isoTimeMatch[2]}:00`, Fablab.timezone).toDate();
6666
};
6767

6868
/**

app/frontend/templates/admin/events/reservations.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ <h1>{{ 'app.admin.event_reservations.the_reservations' | translate }} {{event.ti
3232
<tbody>
3333
<tr ng-repeat="reservation in reservations" ng-class="{'disabled': isCancelled(reservation)}">
3434
<td class="text-c">
35-
<a ui-sref="app.logged.members_show({id: reservation.user_id})">{{ reservation.user_full_name }} </a>
35+
<a ui-sref="app.admin.members_edit({id: reservation.user_id})">{{ reservation.user_full_name }} </a>
3636
</td>
3737
<td>
3838
<span ng-if="event.event_type === 'standard'">{{ reservation.user_full_name }} </span>

app/services/members/members_service.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ def self.handle_organization(params)
103103
params
104104
end
105105

106-
def self.last_registered(limit)
106+
def self.last_registered
107107
query = User.active.with_role(:member)
108108
.includes(:statistic_profile, profile: [:user_avatar])
109109
.where('is_allow_contact = true AND confirmed_at IS NOT NULL')
110110
.order('created_at desc')
111-
.limit(limit)
111+
.limit(10)
112112

113113
# remove unmerged profiles from list
114114
members = query.to_a

app/views/api/members/_member.json.jbuilder

Lines changed: 96 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,119 @@
11
# frozen_string_literal: true
22

3-
json.extract! member, :id, :username, :email, :group_id
4-
json.role member.roles.first.name
3+
json.extract! member, :username, :email, :slug
4+
unless @restricted_member_show
5+
json.id member.id
6+
json.group_id member.group_id
7+
json.role member.roles.first.name
8+
end
59
json.name member.profile.full_name
6-
json.need_completion member.need_completion?
7-
json.ip_address member.current_sign_in_ip.to_s
8-
json.mapped_from_sso member.mapped_from_sso&.split(',')
10+
json.need_completion member.need_completion? unless @restricted_member_show
11+
json.ip_address member.current_sign_in_ip.to_s unless @restricted_member_show
12+
json.mapped_from_sso member.mapped_from_sso&.split(',') unless @restricted_member_show
913

1014
json.profile_attributes do
11-
json.extract! member.profile, :id, :first_name, :last_name, :interest, :software_mastered, :phone, :website, :job
12-
if member.profile.user_avatar
13-
json.user_avatar_attributes do
14-
json.id member.profile.user_avatar.id
15-
json.attachment_url "#{member.profile.user_avatar.attachment_url}?#{member.profile.user_avatar.updated_at.to_i}"
15+
json.first_name member.profile.first_name
16+
json.last_name member.profile.last_name
17+
json.interest member.profile.interest
18+
json.software_mastered member.profile.software_mastered
19+
20+
if @restricted_member_show
21+
json.facebook member.profile.facebook
22+
json.twitter member.profile.twitter
23+
json.viadeo member.profile.viadeo
24+
json.linkedin member.profile.linkedin
25+
json.instagram member.profile.instagram
26+
json.youtube member.profile.youtube
27+
json.vimeo member.profile.vimeo
28+
json.dailymotion member.profile.dailymotion
29+
json.github member.profile.github
30+
json.echosciences member.profile.echosciences
31+
json.pinterest member.profile.pinterest
32+
json.lastfm member.profile.lastfm
33+
json.flickr member.profile.flickr
34+
else
35+
json.id member.profile.id
36+
json.phone member.profile.phone
37+
json.website member.profile.website
38+
json.job member.profile.job
39+
if member.profile.user_avatar
40+
json.user_avatar do
41+
json.id member.profile.user_avatar.id
42+
json.attachment_url "#{member.profile.user_avatar.attachment_url}?#{member.profile.user_avatar.updated_at.to_i}"
43+
end
1644
end
45+
json.extract! member.profile, :facebook, :twitter, :viadeo, :linkedin, :instagram, :youtube, :vimeo, :dailymotion, :github, :echosciences, :pinterest, :lastfm, :flickr
46+
json.tours member.profile.tours&.split || []
1747
end
18-
json.extract! member.profile, :facebook, :twitter, :viadeo, :linkedin, :instagram, :youtube, :vimeo, :dailymotion, :github, :echosciences, :pinterest, :lastfm, :flickr
19-
json.tours member.profile.tours&.split || []
2048
end
2149

22-
json.invoicing_profile_attributes do
23-
json.extract! member.invoicing_profile, :id, :external_id
24-
if member.invoicing_profile.address
25-
json.address_attributes do
26-
json.id member.invoicing_profile.address.id
27-
json.address member.invoicing_profile.address.address
50+
unless @restricted_member_show
51+
json.invoicing_profile_attributes do
52+
json.extract! member.invoicing_profile, :id, :external_id
53+
if member.invoicing_profile.address
54+
json.address_attributes do
55+
json.id member.invoicing_profile.address.id
56+
json.address member.invoicing_profile.address.address
57+
end
2858
end
29-
end
3059

31-
if member.invoicing_profile.organization
32-
json.organization_attributes do
33-
json.extract! member.invoicing_profile.organization, :id, :name
34-
if member.invoicing_profile.organization.address
35-
json.address_attributes do
36-
json.id member.invoicing_profile.organization.address.id
37-
json.address member.invoicing_profile.organization.address.address
60+
if member.invoicing_profile.organization
61+
json.organization_attributes do
62+
json.extract! member.invoicing_profile.organization, :id, :name
63+
if member.invoicing_profile.organization.address
64+
json.address_attributes do
65+
json.id member.invoicing_profile.organization.address.id
66+
json.address member.invoicing_profile.organization.address.address
67+
end
3868
end
3969
end
4070
end
41-
end
4271

43-
json.user_profile_custom_fields_attributes member.invoicing_profile.user_profile_custom_fields
44-
.joins(:profile_custom_field).where('profile_custom_fields.actived' => true).order('profile_custom_fields.id ASC') do |f|
45-
json.id f.id
46-
json.invoicing_profile_id f.invoicing_profile_id
47-
json.profile_custom_field_id f.profile_custom_field_id
48-
json.value f.value
72+
json.user_profile_custom_fields_attributes member.invoicing_profile.user_profile_custom_fields
73+
.joins(:profile_custom_field).where('profile_custom_fields.actived' => true).order('profile_custom_fields.id ASC') do |f|
74+
json.id f.id
75+
json.invoicing_profile_id f.invoicing_profile_id
76+
json.profile_custom_field_id f.profile_custom_field_id
77+
json.value f.value
78+
end
4979
end
50-
end
5180

52-
json.statistic_profile_attributes do
53-
json.id member.statistic_profile.id
54-
json.gender member.statistic_profile.gender.to_s
55-
json.birthday member.statistic_profile&.birthday&.to_date&.iso8601
56-
json.training_ids member.statistic_profile&.training_ids
57-
end
81+
json.statistic_profile_attributes do
82+
json.id member.statistic_profile.id
83+
json.gender member.statistic_profile.gender.to_s
84+
json.birthday member.statistic_profile&.birthday&.to_date&.iso8601
85+
json.training_ids member.statistic_profile&.training_ids
86+
end
5887

59-
if member.subscribed_plan
60-
json.subscribed_plan do
61-
json.partial! 'api/shared/plan', plan: member.subscribed_plan
88+
if member.subscribed_plan
89+
json.subscribed_plan do
90+
json.partial! 'api/shared/plan', plan: member.subscribed_plan
91+
end
6292
end
63-
end
6493

65-
if member.subscription
66-
json.subscription do
67-
json.id member.subscription.id
68-
json.expired_at member.subscription.expired_at.iso8601
69-
json.canceled_at member.subscription.canceled_at.iso8601 if member.subscription.canceled_at
70-
json.plan do # TODO, refactor: duplicates subscribed_plan
71-
json.id member.subscription.plan.id
72-
json.base_name member.subscription.plan.base_name
73-
json.name member.subscription.plan.name
74-
json.interval member.subscription.plan.interval
75-
json.interval_count member.subscription.plan.interval_count
76-
json.amount member.subscription.plan.amount ? (member.subscription.plan.amount / 100.0) : 0
77-
json.monthly_payment member.subscription.plan.monthly_payment
94+
if member.subscription
95+
json.subscription do
96+
json.id member.subscription.id
97+
json.expired_at member.subscription.expired_at.iso8601
98+
json.canceled_at member.subscription.canceled_at.iso8601 if member.subscription.canceled_at
99+
json.plan do
100+
json.id member.subscription.plan.id
101+
json.base_name member.subscription.plan.base_name
102+
json.name member.subscription.plan.name
103+
json.interval member.subscription.plan.interval
104+
json.interval_count member.subscription.plan.interval_count
105+
json.amount member.subscription.plan.amount ? (member.subscription.plan.amount / 100.0) : 0
106+
json.monthly_payment member.subscription.plan.monthly_payment
107+
end
78108
end
79109
end
110+
json.training_credits member.training_credits do |tc|
111+
json.training_id tc.creditable_id
112+
end
113+
json.machine_credits member.machine_credits do |mc|
114+
json.machine_id mc.creditable_id
115+
json.hours_used mc.users_credits.find_by(user_id: member.id).hours_used
116+
end
117+
json.last_sign_in_at member.last_sign_in_at.iso8601 if member.last_sign_in_at
118+
json.validated_at member.validated_at
80119
end
81-
json.training_credits member.training_credits do |tc|
82-
json.training_id tc.creditable_id
83-
end
84-
json.machine_credits member.machine_credits do |mc|
85-
json.machine_id mc.creditable_id
86-
json.hours_used mc.users_credits.find_by(user_id: member.id).hours_used
87-
end
88-
# TODO, missing space_credits?
89-
json.last_sign_in_at member.last_sign_in_at.iso8601 if member.last_sign_in_at
90-
91-
json.validated_at member.validated_at

app/views/api/members/index.json.jbuilder

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,28 @@ user_is_admin = current_user&.admin?
44
max_members = @query.except(:offset, :limit, :order).count
55

66
json.array!(@members) do |member|
7-
json.maxMembers max_members
87
json.id member.id
9-
json.username member.username
10-
json.slug member.slug
11-
json.name member.profile.full_name
12-
json.email member.email if current_user
13-
json.first_name member.profile.first_name
14-
json.last_name member.profile.last_name
15-
json.need_completion member.need_completion?
16-
json.group_id member.group_id
8+
json.maxMembers max_members if !@public_last_subscribed
9+
if @public_last_subscribed
10+
json.name member.profile.full_name
11+
if member.profile.user_avatar
12+
json.avatar do
13+
json.id member.profile.user_avatar.id
14+
json.attachment_url member.profile.user_avatar.attachment_url
15+
end
16+
end
17+
else
18+
json.username member.username
19+
json.slug member.slug
20+
json.name member.profile.full_name
21+
json.email member.email if current_user
22+
json.first_name member.profile.first_name
23+
json.last_name member.profile.last_name
24+
json.need_completion member.need_completion? unless @restricted_member_index
25+
json.group_id member.group_id unless @restricted_member_index
26+
end
1727

18-
if attribute_requested?(@requested_attributes, 'profile')
28+
if !@public_last_subscribed && attribute_requested?(@requested_attributes, 'profile')
1929
json.profile do
2030
if member.profile.user_avatar
2131
json.user_avatar do
@@ -25,7 +35,7 @@ json.array!(@members) do |member|
2535
end
2636
json.first_name member.profile.first_name
2737
json.last_name member.profile.last_name
28-
json.phone member.profile.phone
38+
json.phone member.profile.phone unless @restricted_member_index
2939
end
3040
if user_is_admin
3141
json.statistic_profile do

0 commit comments

Comments
 (0)