diff --git a/CHANGELOG.md b/CHANGELOG.md index f629addf2..47b9503fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable, unreleased changes to this project will be documented in this file. - Update quantity column in Inventory part of Product Variant view - #904 by @dominik-zeglen - Add file attributes - #884 by @orzechdev - Add shipping delivery days - #914 by @orzechdev +- Guard against non-staff users logging in - #947 by @jwm0 # 2.11.1 diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index ef246230d..74e67b150 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3032,6 +3032,9 @@ "src_dot_hooks_dot_3382262667": { "string": "Variant {name} has been set as default." }, + "src_dot_insufficientPermissions": { + "string": "Insufficient permissions" + }, "src_dot_lastName": { "string": "Last Name" }, @@ -6251,6 +6254,9 @@ "src_dot_translations_dot_components_dot_TranslationsVouchersPage_dot_2599922713": { "string": "Voucher Name" }, + "src_dot_unauthorizedDashboardAccess": { + "string": "Only staff users can access the dashboard" + }, "src_dot_unconfirmed": { "context": "order status", "string": "Unconfirmed" diff --git a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har index 8ad594963..e1740a219 100644 --- a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har +++ b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "f515e15cbc83df73e5bd41437971c2e6", + "_id": "a3088678db2635ada66ab049f76c9722", "_order": 0, "cache": {}, "request": { - "bodySize": 691, + "bodySize": 702, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "691" + "value": "702" }, { "_fromType": "array", @@ -56,61 +56,53 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5NjAyODUxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.eo8_Ew98HICB4cFQN2U7mCJ8ydGVOvQLGRT4CnkufMc\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMDYzMjg0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU5BPT0iLCJpc19zdGFmZiI6dHJ1ZX0.QDp4vlm1tKhk8iFnY2MnREvO-IubI5j8g_Wylb1XJqc\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1619, + "bodySize": 1765, "content": { "mimeType": "application/json", - "size": 1619, - "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1596028218, \"exp\": 1596028518, \"token\": \"C3Sk2kLRVuPA\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" + "size": 1765, + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1610632544, \"exp\": 1610632844, \"token\": \"ksEVMvgg6Bfi\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyNA==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyNA==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\", \"name\": \"Manage product types and attributes.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" }, "cookies": [], "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 14:10:40 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", - "value": "1619" + "value": "1765" }, { "name": "x-content-type-options", "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" } ], - "headersSize": 336, + "headersSize": 194, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.327Z", - "time": 23, + "startedDateTime": "2021-01-14T14:10:40.434Z", + "time": 155, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 23 + "wait": 155 } } ], diff --git a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har index be977fd9c..0ac2a2d6a 100644 --- a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har +++ b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "7c460842cac4a92c188d5451dfc533a2", + "_id": "a2b8a02f624e52cd2b73a831f65d9a52", "_order": 0, "cache": {}, "request": { - "bodySize": 587, + "bodySize": 598, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "587" + "value": "598" }, { "_fromType": "array", @@ -56,74 +56,65 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1830, + "bodySize": 1976, "content": { "mimeType": "application/json", - "size": 1830, - "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"rLPNMGNYKXH8VY4UNEWl4nEOFMseocljioigPl36IM2CqbdmOTEpNwvdHBAJ1ZWQ\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5NjAyODUxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.eo8_Ew98HICB4cFQN2U7mCJ8ydGVOvQLGRT4CnkufMc\", \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + "size": 1976, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"UIzzJSFalS8pplfM1j5QNIUNiXb0VFH3kbe6kfTddYvLjJ9DhMasCtHJKXoGDfbw\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMDYzMjg0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU5BPT0iLCJpc19zdGFmZiI6dHJ1ZX0.QDp4vlm1tKhk8iFnY2MnREvO-IubI5j8g_Wylb1XJqc\", \"user\": {\"id\": \"VXNlcjoyNA==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": true, \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES\", \"name\": \"Manage product types and attributes.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" }, "cookies": [ { "httpOnly": true, "name": "refreshToken", "path": "/", - "secure": true, - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5ODYyMDIxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6InJMUE5NR05ZS1hIOFZZNFVORVdsNG5FT0ZNc2VvY2xqaW9pZ1BsMzZJTTJDcWJkbU9URXBOd3ZkSEJBSjFaV1EifQ.boD8G4pkSnZF-PLl5oOg85Uj-mqTiAzOkua9aAG3Bz4" + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ" } ], "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 13:55:44 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", - "value": "1830" + "value": "1976" }, { "name": "x-content-type-options", "value": "nosniff" }, + { + "name": "referrer-policy", + "value": "same-origin" + }, { "_fromType": "array", "name": "set-cookie", - "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5ODYyMDIxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6InJMUE5NR05ZS1hIOFZZNFVORVdsNG5FT0ZNc2VvY2xqaW9pZ1BsMzZJTTJDcWJkbU9URXBOd3ZkSEJBSjFaV1EifQ.boD8G4pkSnZF-PLl5oOg85Uj-mqTiAzOkua9aAG3Bz4; HttpOnly; Path=/; Secure" + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2MzI1NDQsImV4cCI6MTYxMzIyNDU0NCwidG9rZW4iOiJrc0VWTXZnZzZCZmkiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lOQT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6IlVJenpKU0ZhbFM4cHBsZk0xajVRTklVTmlYYjBWRkgza2JlNmtmVGRkWXZMako5RGhNYXNDdEhKS1hvR0RmYncifQ.Br0GWGPPcnysyUxukjBBfXNbwCAm2qlR5OYClwFF3ZQ; HttpOnly; Path=/" } ], - "headersSize": 768, + "headersSize": 618, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.064Z", - "time": 118, + "startedDateTime": "2021-01-14T13:55:44.094Z", + "time": 392, "timings": { "blocked": -1, "connect": -1, @@ -131,7 +122,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 118 + "wait": 392 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har index 8e4ea3f81..a86975194 100644 --- a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "4836098613648775386c1e10728424dd", + "_id": "b1557b45bbbf7aed1a4a53f5141ca324", "_order": 0, "cache": {}, "request": { - "bodySize": 428, + "bodySize": 439, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "428" + "value": "439" }, { "_fromType": "array", @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"NotAToken\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"NotAToken\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -72,28 +72,16 @@ "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 14:10:40 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", "value": "89" @@ -101,16 +89,20 @@ { "name": "x-content-type-options", "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" } ], - "headersSize": 334, + "headersSize": 192, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.368Z", - "time": 6, + "startedDateTime": "2021-01-14T14:10:40.611Z", + "time": 25, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 6 + "wait": 25 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har b/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har new file mode 100644 index 000000000..6cc007de4 --- /dev/null +++ b/recordings/User_3768991250/will-not-be-logged-if-is-non-staff_821531868/recording.har @@ -0,0 +1,132 @@ +{ + "log": { + "_recordingName": "User/will not be logged if is non-staff", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "0b09ec35ecae5b17a2ccda062b1d6ef5", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 602, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "602" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"client@example.com\",\"password\":\"password\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 616, + "content": { + "mimeType": "application/json", + "size": 616, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"Gac5v8mZt6dW0HBXp5RNt8GAWciTbVzsycpqtUKV797npCXajke5h9VoF4l9MreP\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMDY5ODkzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiVlhObGNqb3pNQT09IiwiaXNfc3RhZmYiOmZhbHNlfQ.RVYwqQSPEZoi2E_ImC30Ml37RJ2Fu6AnSmfDkAYMcqY\", \"user\": {\"id\": \"VXNlcjozMA==\", \"email\": \"client@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"isStaff\": false, \"userPermissions\": [], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + }, + "cookies": [ + { + "httpOnly": true, + "name": "refreshToken", + "path": "/", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMzI5MDYzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiR2FjNXY4bVp0NmRXMEhCWHA1Uk50OEdBV2NpVGJWenN5Y3BxdFVLVjc5N25wQ1hhamtlNWg5Vm9GNGw5TXJlUCJ9.jUF_9vvtwT8EUbQ4GM7u0YVivk7TiSoSecHDZ0jJ2MI" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 15 Jan 2021 08:17:13 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.7" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "616" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA2OTg2MzMsImV4cCI6MTYxMzI5MDYzMywidG9rZW4iOiJ6MDc2QndLZkNEMmYiLCJlbWFpbCI6ImNsaWVudEBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam96TUE9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiR2FjNXY4bVp0NmRXMEhCWHA1Uk50OEdBV2NpVGJWenN5Y3BxdFVLVjc5N25wQ1hhamtlNWg5Vm9GNGw5TXJlUCJ9.jUF_9vvtwT8EUbQ4GM7u0YVivk7TiSoSecHDZ0jJ2MI; HttpOnly; Path=/" + } + ], + "headersSize": 619, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2021-01-15T08:17:12.850Z", + "time": 623, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 623 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index 547653fee..174fede4a 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "86487093ff8b070d496fcdc566e01adf", + "_id": "d94d7821dc951e48c410d691d7eccdef", "_order": 0, "cache": {}, "request": { - "bodySize": 603, + "bodySize": 614, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "603" + "value": "614" }, { "_fromType": "array", @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n isStaff\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -72,28 +72,16 @@ "headers": [ { "name": "date", - "value": "Wed, 29 Jul 2020 13:10:18 GMT" + "value": "Thu, 14 Jan 2021 13:55:45 GMT" }, { "name": "server", - "value": "WSGIServer/0.2 CPython/3.8.1" + "value": "WSGIServer/0.2 CPython/3.8.7" }, { "name": "content-type", "value": "application/json" }, - { - "name": "access-control-allow-origin", - "value": "http://localhost:9000" - }, - { - "name": "access-control-allow-methods", - "value": "POST, OPTIONS" - }, - { - "name": "access-control-allow-headers", - "value": "Origin, Content-Type, Accept, Authorization" - }, { "name": "content-length", "value": "214" @@ -101,16 +89,20 @@ { "name": "x-content-type-options", "value": "nosniff" + }, + { + "name": "referrer-policy", + "value": "same-origin" } ], - "headersSize": 335, + "headersSize": 193, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-29T13:10:18.208Z", - "time": 99, + "startedDateTime": "2021-01-14T13:55:44.521Z", + "time": 1183, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +110,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 99 + "wait": 1183 } } ], diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index c9d82f557..058ad8b08 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -20,12 +20,17 @@ function renderAuthProvider(apolloClient: ApolloClient) { return result; } -const credentials = { +const adminCredentials = { email: "admin@example.com", password: "admin", token: null }; +const nonStaffUserCredentials = { + email: "client@example.com", + password: "password" +}; + beforeEach(() => { localStorage.clear(); sessionStorage.clear(); @@ -36,10 +41,10 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => - hook.current.login(credentials.email, credentials.password) + hook.current.login(adminCredentials.email, adminCredentials.password) ); - expect(hook.current.userContext.email).toBe(credentials.email); - credentials.token = getTokens().auth; + expect(hook.current.userContext.email).toBe(adminCredentials.email); + adminCredentials.token = getTokens().auth; done(); }); @@ -48,19 +53,33 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => - hook.current.login(credentials.email, "NotAValidPassword123!") + hook.current.login(adminCredentials.email, "NotAValidPassword123!") ); expect(hook.current.userContext).toBe(null); done(); }); + it("will not be logged in if is non-staff", async done => { + const hook = renderAuthProvider(apolloClient); + + await act(() => + hook.current.login( + nonStaffUserCredentials.email, + nonStaffUserCredentials.password + ) + ); + expect(hook.current.userContext).toBe(undefined); + + done(); + }); + it("will be logged if has valid token", async done => { - setAuthToken(credentials.token, false); + setAuthToken(adminCredentials.token, false); const hook = renderAuthProvider(apolloClient); await act(() => hook.current.autologinPromise.current); - expect(hook.current.userContext.email).toBe(credentials.email); + expect(hook.current.userContext.email).toBe(adminCredentials.email); done(); }); diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index ad83dcea8..37b75a5b9 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -2,6 +2,7 @@ import { IMessageContext } from "@saleor/components/messages"; import { DEMO_MODE } from "@saleor/config"; import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; +import { commonMessages } from "@saleor/intl"; import { getMutationStatus } from "@saleor/misc"; import { isSupported as isCredentialsManagementAPISupported, @@ -41,6 +42,26 @@ export function useAuthProvider( const autologinPromise = useRef>(); const refreshPromise = useRef>(); + useEffect(() => { + const token = getTokens().auth; + if (!!token && !userContext) { + autologinPromise.current = tokenVerify({ variables: { token } }); + } else { + autologinPromise.current = loginWithCredentialsManagementAPI(login); + } + }, []); + + useEffect(() => { + if (userContext && !userContext.isStaff) { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.unauthorizedDashboardAccess), + title: intl.formatMessage(commonMessages.insufficientPermissions) + }); + } + }, [userContext]); + const logout = () => { setUserContext(undefined); if (isCredentialsManagementAPISupported) { @@ -54,22 +75,18 @@ export function useAuthProvider( TokenAuthVariables >(tokenAuthMutation, { client: apolloClient, - onCompleted: result => { - if (result.tokenCreate.errors.length > 0) { + onCompleted: ({ tokenCreate }) => { + if (tokenCreate.errors.length > 0) { logout(); } - const user = result.tokenCreate.user; + const user = tokenCreate.user; // FIXME: Now we set state also when auth fails and returned user is // `null`, because the LoginView uses this `null` to display error. setUserContext(user); if (user) { - setTokens( - result.tokenCreate.token, - result.tokenCreate.csrfToken, - persistToken - ); + setTokens(tokenCreate.token, tokenCreate.csrfToken, persistToken); } }, onError: logout @@ -115,15 +132,6 @@ export function useAuthProvider( } }; - useEffect(() => { - const token = getTokens().auth; - if (!!token && !userContext) { - autologinPromise.current = tokenVerify({ variables: { token } }); - } else { - autologinPromise.current = loginWithCredentialsManagementAPI(login); - } - }, []); - const login = async (email: string, password: string) => { const result = await tokenAuth({ variables: { email, password } }); diff --git a/src/auth/types/SetPassword.ts b/src/auth/types/SetPassword.ts index 0ede71f38..2a1d539c8 100644 --- a/src/auth/types/SetPassword.ts +++ b/src/auth/types/SetPassword.ts @@ -31,6 +31,7 @@ export interface SetPassword_setPassword_user { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (SetPassword_setPassword_user_userPermissions | null)[] | null; avatar: SetPassword_setPassword_user_avatar | null; } diff --git a/src/auth/types/TokenAuth.ts b/src/auth/types/TokenAuth.ts index 8da32e338..07272b217 100644 --- a/src/auth/types/TokenAuth.ts +++ b/src/auth/types/TokenAuth.ts @@ -31,6 +31,7 @@ export interface TokenAuth_tokenCreate_user { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (TokenAuth_tokenCreate_user_userPermissions | null)[] | null; avatar: TokenAuth_tokenCreate_user_avatar | null; } diff --git a/src/auth/types/VerifyToken.ts b/src/auth/types/VerifyToken.ts index 10ece6970..06934e963 100644 --- a/src/auth/types/VerifyToken.ts +++ b/src/auth/types/VerifyToken.ts @@ -25,6 +25,7 @@ export interface VerifyToken_tokenVerify_user { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (VerifyToken_tokenVerify_user_userPermissions | null)[] | null; avatar: VerifyToken_tokenVerify_user_avatar | null; } diff --git a/src/components/AppLayout/AppChannelContext.tsx b/src/components/AppLayout/AppChannelContext.tsx index 930cacfd0..3bfb62fa1 100644 --- a/src/components/AppLayout/AppChannelContext.tsx +++ b/src/components/AppLayout/AppChannelContext.tsx @@ -33,8 +33,8 @@ export const AppChannelProvider: React.FC = ({ children }) => { const [isPickerActive, setPickerActive] = React.useState(false); React.useEffect(() => { - if (!selectedChannel) { - setSelectedChannel(channelData?.channels[0].id); + if (!selectedChannel && channelData?.channels) { + setSelectedChannel(channelData.channels[0].id); } }, [channelData]); diff --git a/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx b/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx index 3395d2e91..262d18eaa 100644 --- a/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx +++ b/src/components/ChannelsAvailability/ChannelsAvailability.stories.tsx @@ -17,6 +17,7 @@ const user: User = { email: "email@example.com", firstName: "User", id: "123", + isStaff: true, lastName: "User", userPermissions: [ { diff --git a/src/fragments/auth.ts b/src/fragments/auth.ts index 2a897a523..c09d611eb 100644 --- a/src/fragments/auth.ts +++ b/src/fragments/auth.ts @@ -6,6 +6,7 @@ export const fragmentUser = gql` email firstName lastName + isStaff userPermissions { code name diff --git a/src/fragments/types/User.ts b/src/fragments/types/User.ts index d504cc3e2..67c1e5660 100644 --- a/src/fragments/types/User.ts +++ b/src/fragments/types/User.ts @@ -25,6 +25,7 @@ export interface User { email: string; firstName: string; lastName: string; + isStaff: boolean; userPermissions: (User_userPermissions | null)[] | null; avatar: User_avatar | null; } diff --git a/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx b/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx index a217306c4..918fd4646 100644 --- a/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx +++ b/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx @@ -95,16 +95,21 @@ const HomeNotificationTable: React.FC = props => { {noChannel && ( - - - - {intl.formatMessage(messages.createNewChannel)} - - - - - - + + + + + {intl.formatMessage(messages.createNewChannel)} + + + + + + + )}