From 58cddf312f96d78d641be6e2084077e41ff51d75 Mon Sep 17 00:00:00 2001 From: The Things Bot Date: Thu, 23 Jan 2025 19:49:53 +0000 Subject: [PATCH 1/9] all: Bump to version 3.33.1 --- data/lorawan-devices | 2 +- data/lorawan-webhook-templates | 2 +- package.json | 2 +- pkg/version/ttn.go | 2 +- sdk/js/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/lorawan-devices b/data/lorawan-devices index d3e556caf0..a1353d71e7 160000 --- a/data/lorawan-devices +++ b/data/lorawan-devices @@ -1 +1 @@ -Subproject commit d3e556caf03229dbad7458441e28d0674fab22aa +Subproject commit a1353d71e7594486b1b566af530d7ccb0c5420be diff --git a/data/lorawan-webhook-templates b/data/lorawan-webhook-templates index f725e328cd..9f5b744a87 160000 --- a/data/lorawan-webhook-templates +++ b/data/lorawan-webhook-templates @@ -1 +1 @@ -Subproject commit f725e328cdbcc17bf69bbe6d3ab513c0abd79a6e +Subproject commit 9f5b744a873b87ef86bfb7b462d660ea2295e94f diff --git a/package.json b/package.json index 9bc69f831c..8f6c441941 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ttn-stack", - "version": "3.33.0", + "version": "3.33.1", "description": "The Things Stack", "main": "index.js", "repository": "https://github.com/TheThingsNetwork/lorawan-stack.git", diff --git a/pkg/version/ttn.go b/pkg/version/ttn.go index 1a40295184..77546f9240 100644 --- a/pkg/version/ttn.go +++ b/pkg/version/ttn.go @@ -3,4 +3,4 @@ package version // TTN Version -var TTN = "3.33.0-dev" +var TTN = "3.33.1-dev" diff --git a/sdk/js/package.json b/sdk/js/package.json index d0d4c69482..ecc672d97c 100644 --- a/sdk/js/package.json +++ b/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "ttn-lw", - "version": "3.33.0", + "version": "3.33.1", "description": "The Things Stack for LoRaWAN JavaScript SDK", "url": "https://github.com/TheThingsNetwork/lorawan-stack/tree/default/sdk/js", "main": "dist/index.js", From cfb251873c46b25d7fb00428e13d46aa79922d6f Mon Sep 17 00:00:00 2001 From: Krishna Iyer Date: Tue, 28 Jan 2025 13:17:55 +0100 Subject: [PATCH 2/9] Merge pull request #7489 from TheThingsNetwork/fix/client-support-user-rights-limitations Impose support user rights limitation on GetAccessToken operation --- pkg/identityserver/bunstore/oauth_store.go | 5 ++ pkg/identityserver/bunstore/store_test.go | 1 + pkg/identityserver/storetest/oauth_store.go | 61 +++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/pkg/identityserver/bunstore/oauth_store.go b/pkg/identityserver/bunstore/oauth_store.go index 025e992ad4..527b1043b4 100644 --- a/pkg/identityserver/bunstore/oauth_store.go +++ b/pkg/identityserver/bunstore/oauth_store.go @@ -685,6 +685,11 @@ func (s *oauthStore) GetAccessToken(ctx context.Context, id string) (*ttnpb.OAut return nil, err } + // NOTE: This imposes a limitation on the client's rights if the token's user is the unique support user. + if model.User.Account.UID == ttnpb.SupportUserID { + model.Rights = convertIntSlice[ttnpb.Right, int](ttnpb.AllReadAdminRights.GetRights()) + } + pb, err := accessTokenToPB(model, nil, nil) if err != nil { return nil, err diff --git a/pkg/identityserver/bunstore/store_test.go b/pkg/identityserver/bunstore/store_test.go index 746bcbe0d7..f1a8fc7148 100644 --- a/pkg/identityserver/bunstore/store_test.go +++ b/pkg/identityserver/bunstore/store_test.go @@ -185,6 +185,7 @@ func TestOAuthStore(t *testing.T) { st := storetest.New(t, newTestStore) st.TestOAuthStore(t) + st.TestOAuthStoreSupportUser(t) st.TestOAuthStorePagination(t) st.TestOAuthStorePaginationDefaults(t) } diff --git a/pkg/identityserver/storetest/oauth_store.go b/pkg/identityserver/storetest/oauth_store.go index b6cf59f01e..df9bdeacd6 100644 --- a/pkg/identityserver/storetest/oauth_store.go +++ b/pkg/identityserver/storetest/oauth_store.go @@ -356,6 +356,67 @@ func (st *StoreTest) TestOAuthStore(t *T) { }) } +// TestOAuthStoreSupportUser tests the behavior of AccessTokens with the unique support userID. +func (st *StoreTest) TestOAuthStoreSupportUser(t *T) { + supportUsr := st.population.NewUser() + supportUsr.Ids.UserId = ttnpb.SupportUserID + supportUsr.Name = "support" + supportUsr.PrimaryEmailAddress = "support@dummy-email.com" + + ses1 := st.population.NewUserSession(supportUsr.GetIds()) + cli1 := st.population.NewClient(nil) + cli1.SkipAuthorization = true + + s, ok := st.PrepareDB(t).(interface { + Store + is.OAuthStore + }) + defer st.DestroyDB(t, true, "users", "accounts", "user_sessions", "clients") + if !ok { + t.Skip("Store does not implement OAuthStore") + } + defer s.Close() + + var createdAccessToken *ttnpb.OAuthAccessToken + + t.Run("CreateAccessToken", func(t *T) { + a, ctx := test.New(t) + var err error + start := time.Now().Truncate(time.Second) + + createdAccessToken, err = s.CreateAccessToken(ctx, &ttnpb.OAuthAccessToken{ + UserIds: supportUsr.GetIds(), + UserSessionId: ses1.GetSessionId(), + ClientIds: cli1.GetIds(), + Id: "token_id", + AccessToken: "access_token", + RefreshToken: "refresh_token", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ALL}, + ExpiresAt: timestamppb.New(start.Add(5 * time.Minute)), + }, "") + if a.So(err, should.BeNil) && a.So(createdAccessToken, should.NotBeNil) { + a.So(createdAccessToken.UserIds, should.Resemble, supportUsr.GetIds()) + a.So(createdAccessToken.UserSessionId, should.Equal, ses1.GetSessionId()) + a.So(createdAccessToken.ClientIds, should.Resemble, cli1.GetIds()) + a.So(createdAccessToken.Id, should.Equal, "token_id") + a.So(createdAccessToken.AccessToken, should.Equal, "access_token") + a.So(createdAccessToken.RefreshToken, should.Equal, "refresh_token") + a.So(createdAccessToken.Rights, should.Resemble, []ttnpb.Right{ttnpb.Right_RIGHT_ALL}) + a.So(*ttnpb.StdTime(createdAccessToken.ExpiresAt), should.Equal, start.Add(5*time.Minute)) + a.So(*ttnpb.StdTime(createdAccessToken.CreatedAt), should.HappenWithin, 5*time.Second, start) + } + }) + + t.Run("GetAccessToken", func(t *T) { + a, ctx := test.New(t) + got, err := s.GetAccessToken(ctx, "token_id") + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + // NOTE: This should be limited due to referencing the unique support userID. + a.So(got.Rights, should.Resemble, ttnpb.AllReadAdminRights.GetRights()) + } + }) +} + func (st *StoreTest) TestOAuthStorePagination(t *T) { a, ctx := test.New(t) From 4b531c7fc6c7f97fba08f0d49b1e58758b808fdd Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 28 Jan 2025 11:53:49 +0100 Subject: [PATCH 3/9] console: Add permission checks to every add action --- pkg/webui/components/header/index.js | 27 +-- .../components/sidebar/section-label/index.js | 51 +++--- pkg/webui/console/containers/header/index.js | 44 +++-- .../containers/shortcut-panel/index.js | 168 ++++++++++++------ .../all-top-entities/index.js | 42 +++-- .../top-applications/index.js | 21 ++- .../top-gateways/index.js | 11 +- .../console/containers/users-table/index.js | 4 +- pkg/webui/console/lib/feature-checks.js | 18 ++ 9 files changed, 261 insertions(+), 125 deletions(-) diff --git a/pkg/webui/components/header/index.js b/pkg/webui/components/header/index.js index f07e1c2d1b..931a9c8021 100644 --- a/pkg/webui/components/header/index.js +++ b/pkg/webui/components/header/index.js @@ -45,6 +45,7 @@ const Header = ({ toggleSidebarMinimized, expandSidebar, handleHideSidebar, + hasCreateRights, ...rest }) => { const LinkComponent = safe ? 'a' : Link @@ -94,16 +95,19 @@ const Header = ({ {!safe && (
-
- - + {mayCreateGtws && ( + + )} + {mayCreateApps && ( + + )} } diff --git a/pkg/webui/console/containers/top-entities-dashboard-panel/top-applications/index.js b/pkg/webui/console/containers/top-entities-dashboard-panel/top-applications/index.js index 1d2a8f8b4c..4750c36503 100644 --- a/pkg/webui/console/containers/top-entities-dashboard-panel/top-applications/index.js +++ b/pkg/webui/console/containers/top-entities-dashboard-panel/top-applications/index.js @@ -27,7 +27,10 @@ import LastSeen from '@console/components/last-seen' import sharedMessages from '@ttn-lw/lib/shared-messages' +import { checkFromState, mayCreateApplications } from '@console/lib/feature-checks' + import { selectApplicationTopEntities } from '@console/store/selectors/top-entities' +import { selectUser } from '@console/store/selectors/user' import EntitiesList from '../list' @@ -38,6 +41,10 @@ const m = defineMessages({ const TopApplicationsList = () => { const items = useSelector(selectApplicationTopEntities) + const user = useSelector(selectUser) + const mayCreateApps = useSelector(state => + user ? checkFromState(mayCreateApplications, state) : false, + ) const headers = [ { @@ -104,12 +111,14 @@ const TopApplicationsList = () => { />
- + {mayCreateApps && ( + + )}
} diff --git a/pkg/webui/console/containers/top-entities-dashboard-panel/top-gateways/index.js b/pkg/webui/console/containers/top-entities-dashboard-panel/top-gateways/index.js index 7f94104755..24eeef2c02 100644 --- a/pkg/webui/console/containers/top-entities-dashboard-panel/top-gateways/index.js +++ b/pkg/webui/console/containers/top-entities-dashboard-panel/top-gateways/index.js @@ -24,7 +24,10 @@ import Message from '@ttn-lw/lib/components/message' import sharedMessages from '@ttn-lw/lib/shared-messages' +import { checkFromState, mayCreateGateways } from '@console/lib/feature-checks' + import { selectGatewayTopEntities } from '@console/store/selectors/top-entities' +import { selectUser } from '@console/store/selectors/user' import EntitiesList from '../list' @@ -36,6 +39,10 @@ const m = defineMessages({ const TopGatewaysList = () => { const items = useSelector(selectGatewayTopEntities) + const user = useSelector(selectUser) + const mayCreateGtws = useSelector(state => + user ? checkFromState(mayCreateGateways, state) : false, + ) const headers = [ { @@ -95,7 +102,9 @@ const TopGatewaysList = () => { />
- + {mayCreateGtws && ( + + )}
} diff --git a/pkg/webui/console/containers/users-table/index.js b/pkg/webui/console/containers/users-table/index.js index 81e24bc2cd..4a812db040 100644 --- a/pkg/webui/console/containers/users-table/index.js +++ b/pkg/webui/console/containers/users-table/index.js @@ -116,6 +116,8 @@ const UsersTable = props => { const dispatch = useDispatch() const currentUserId = useSelector(selectUserId) const mayInvite = useSelector(state => checkFromState(maySendInvites, state)) + const mayPerformAllActions = useSelector(state => checkFromState(mayPerformAllUserActions, state)) + const mayCreate = useSelector(state => checkFromState(mayCreateUsers, state)) const tabsWithInvitations = [ ...tabs, @@ -335,7 +337,7 @@ const UsersTable = props => { (users, totalCount) => ({ users, totalCount, - mayAdd: mayManageUsers && (mayPerformAllUserActions || mayCreateUsers), + mayAdd: mayManageUsers && (mayPerformAllActions || mayCreate), }), ) diff --git a/pkg/webui/console/lib/feature-checks.js b/pkg/webui/console/lib/feature-checks.js index f9a69b6089..ebc637a90b 100644 --- a/pkg/webui/console/lib/feature-checks.js +++ b/pkg/webui/console/lib/feature-checks.js @@ -46,6 +46,10 @@ export const mayCreateApplications = { rightsSelector: selectUserRights, check: rights => rights.includes('RIGHT_USER_APPLICATIONS_CREATE'), } +export const mayCreateDevices = { + rightsSelector: selectUserRights, + check: rights => rights.includes('RIGHT_APPLICATION_DEVICES_WRITE'), +} export const mayViewGatewaysOfUser = { rightsSelector: selectUserRights, check: rights => rights.includes('RIGHT_USER_GATEWAYS_LIST'), @@ -325,6 +329,20 @@ export const mayWriteTraffic = { check: rights => mayScheduleDownlinks.check(rights) || maySendUplink.check(rights), } +export const mayCreateEntities = { + rightsSelector: createSelector( + [ + selectUserRights, + selectOrganizationRights, + selectApplicationRights, + selectClientRights, + selectGatewayRights, + ], + (user, org, app, client, gateway) => [...user, ...org, ...app, ...client, ...gateway], + ), + check: rights => rights.some(right => right.includes('_CREATE')), +} + // Pub/Sub feature checks. export const mayAddPubSubIntegrations = { check: (natsDisabled, mqttDisabled) => !natsDisabled || !mqttDisabled, From 1568d5ec531e758a50f348faaaf78aa92c144913 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 28 Jan 2025 13:46:37 +0100 Subject: [PATCH 4/9] console: Fix linting --- pkg/webui/console/containers/shortcut-panel/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/webui/console/containers/shortcut-panel/index.js b/pkg/webui/console/containers/shortcut-panel/index.js index 142ca1e42f..8122b84b67 100644 --- a/pkg/webui/console/containers/shortcut-panel/index.js +++ b/pkg/webui/console/containers/shortcut-panel/index.js @@ -15,7 +15,6 @@ import React from 'react' import { defineMessages } from 'react-intl' import { useDispatch, useSelector } from 'react-redux' -import classNames from 'classnames' import { APPLICATION } from '@console/constants/entities' From 72a7c271156cf000a84ff53dbd20cb384751f3b6 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 28 Jan 2025 13:53:11 +0100 Subject: [PATCH 5/9] console: Fix import --- pkg/webui/console/containers/shortcut-panel/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/webui/console/containers/shortcut-panel/index.js b/pkg/webui/console/containers/shortcut-panel/index.js index 8122b84b67..142ca1e42f 100644 --- a/pkg/webui/console/containers/shortcut-panel/index.js +++ b/pkg/webui/console/containers/shortcut-panel/index.js @@ -15,6 +15,7 @@ import React from 'react' import { defineMessages } from 'react-intl' import { useDispatch, useSelector } from 'react-redux' +import classNames from 'classnames' import { APPLICATION } from '@console/constants/entities' From c1662736d583c91bde720d31900ae0de8f35fabe Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 28 Jan 2025 14:53:59 +0100 Subject: [PATCH 6/9] console: Fix panel --- pkg/webui/components/header/index.js | 2 +- .../containers/shortcut-panel/index.js | 117 +++++++----------- 2 files changed, 48 insertions(+), 71 deletions(-) diff --git a/pkg/webui/components/header/index.js b/pkg/webui/components/header/index.js index 931a9c8021..523e87f9b4 100644 --- a/pkg/webui/components/header/index.js +++ b/pkg/webui/components/header/index.js @@ -198,7 +198,7 @@ Header.defaultProps = { profileDropdownItems: undefined, onMenuClick: () => null, handleHideSidebar: () => null, - hasCreateRights: true, + hasCreateRights: false, } export default Header diff --git a/pkg/webui/console/containers/shortcut-panel/index.js b/pkg/webui/console/containers/shortcut-panel/index.js index 142ca1e42f..6ae5148043 100644 --- a/pkg/webui/console/containers/shortcut-panel/index.js +++ b/pkg/webui/console/containers/shortcut-panel/index.js @@ -81,6 +81,39 @@ const ShortcutPanel = ({ panelClassName, mobile }) => { dispatch(setSearchOpen(true)) }, [dispatch]) + const shortcutItems = [ + { + hasRight: mayCreateApps, + icon: IconApplication, + title: sharedMessages.createApplication, + link: '/applications/add', + }, + { + hasRight: mayCreateDev, + icon: IconDevice, + title: m.addEndDevice, + action: handleRegisterDeviceClick, + }, + { + hasRight: mayCreateOrgs, + icon: IconUsersGroup, + title: sharedMessages.createOrganization, + link: '/organizations/add', + }, + { + hasRight: mayCreateKeys, + icon: IconKey, + title: sharedMessages.addApiKey, + link: '/user-settings/api-keys/add', + }, + { + hasRight: mayCreateGtws, + icon: IconGateway, + title: sharedMessages.registerGateway, + link: '/gateways/add', + } + ] + if (!hasCreateRights) { return null } @@ -88,46 +121,16 @@ const ShortcutPanel = ({ panelClassName, mobile }) => { if (mobile) { return (
- {mayCreateApps && ( - - )} - {mayCreateDev && ( + {shortcutItems.filter((item) => item.hasRight).map(({ icon, title, link, action }, index) => ( - )} - {mayCreateOrgs && ( - - )} - {mayCreateKeys && ( - - )} - {mayCreateGtws && ( - - )} + ))}
) } @@ -135,41 +138,15 @@ const ShortcutPanel = ({ panelClassName, mobile }) => { return (
- {mayCreateApps && ( - - )} - {mayCreateDevices && ( - - )} - {mayCreateOrgs && ( - - )} - {mayCreateKeys && ( - - )} - {mayCreateGtws && ( + {shortcutItems.filter((item) => item.hasRight).map(({ icon, title, link, action }, index) => ( - )} + ))}
) From df4813f9efdde0a38b7998cfc4706024c40a2600 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 28 Jan 2025 15:00:10 +0100 Subject: [PATCH 7/9] console: Fix linting --- .../containers/shortcut-panel/index.js | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/pkg/webui/console/containers/shortcut-panel/index.js b/pkg/webui/console/containers/shortcut-panel/index.js index 6ae5148043..ea0169a763 100644 --- a/pkg/webui/console/containers/shortcut-panel/index.js +++ b/pkg/webui/console/containers/shortcut-panel/index.js @@ -111,7 +111,7 @@ const ShortcutPanel = ({ panelClassName, mobile }) => { icon: IconGateway, title: sharedMessages.registerGateway, link: '/gateways/add', - } + }, ] if (!hasCreateRights) { @@ -121,16 +121,18 @@ const ShortcutPanel = ({ panelClassName, mobile }) => { if (mobile) { return (
- {shortcutItems.filter((item) => item.hasRight).map(({ icon, title, link, action }, index) => ( - - ))} + {shortcutItems + .filter(item => item.hasRight) + .map(({ icon, title, link, action }, index) => ( + + ))}
) } @@ -138,15 +140,11 @@ const ShortcutPanel = ({ panelClassName, mobile }) => { return (
- {shortcutItems.filter((item) => item.hasRight).map(({ icon, title, link, action }, index) => ( - - ))} + {shortcutItems + .filter(item => item.hasRight) + .map(({ icon, title, link, action }, index) => ( + + ))}
) From 7319d337e137ad3f3076b73cf32a9a337fbc369f Mon Sep 17 00:00:00 2001 From: Imre Halasz Date: Tue, 28 Jan 2025 15:15:40 +0100 Subject: [PATCH 8/9] ns: Add `battery_percentage` to the get paths --- pkg/networkserver/grpc_gsns.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/networkserver/grpc_gsns.go b/pkg/networkserver/grpc_gsns.go index 53f731e140..88ab798398 100644 --- a/pkg/networkserver/grpc_gsns.go +++ b/pkg/networkserver/grpc_gsns.go @@ -857,6 +857,7 @@ var handleDataUplinkGetPaths = [...]string{ "supports_class_b", "supports_class_c", "supports_join", + "battery_percentage", } // mergeMetadata merges the metadata collected for up. From d9c0c7fb7054f7d28ee6aa1645ec8cf767a7e796 Mon Sep 17 00:00:00 2001 From: The Things Bot Date: Mon, 10 Feb 2025 11:03:39 +0000 Subject: [PATCH 9/9] all: Enter release date of version 3.33.1 into the changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca4093292..be661dd41a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ For details about compatibility between different releases, see the **Commitment ### Security -## [3.33.1] - unreleased +## [3.33.1] - 2025-02-10 ### Added