Skip to content

Commit

Permalink
feat: Add access control all over codebase (#281)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeeva Ramachandran <[email protected]>
  • Loading branch information
Pritish Budhiraja and JeevaRamu0104 authored Feb 6, 2024
1 parent 1c8109a commit 7c6beec
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 35 deletions.
29 changes: 29 additions & 0 deletions src/components/ACLDiv.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@react.component
let make = (
~permission,
~onClick,
~children,
~className="",
~noAccessDescription=?,
~description=?,
~tooltipWidthClass=?,
~isRelative=?,
~contentAlign=?,
~justifyClass=?,
~tooltipForWidthClass=?,
) => {
<ACLToolTip
access=permission
?noAccessDescription
?tooltipForWidthClass
?description
?isRelative
?contentAlign
?tooltipWidthClass
?justifyClass
toolTipFor={<div className onClick={permission === AuthTypes.Access ? onClick : {_ => ()}}>
{children}
</div>}
toolTipPosition={Top}
/>
}
14 changes: 14 additions & 0 deletions src/components/ACLDiv.resi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@react.component
let make: (
~permission: AuthTypes.authorization,
~onClick: JsxEvent.Mouse.t => unit,
~children: React.element,
~className: string=?,
~noAccessDescription: string=?,
~description: string=?,
~tooltipWidthClass: string=?,
~isRelative: bool=?,
~contentAlign: ToolTip.contentPosition=?,
~justifyClass: string=?,
~tooltipForWidthClass: string=?,
) => React.element
58 changes: 58 additions & 0 deletions src/components/tooltip/ACLToolTip.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
open ToolTip

@react.component
let make = (
~access,
~noAccessDescription=HSwitchUtils.noAccessControlText,
~description="",
~descriptionComponent=React.null,
~tooltipPositioning: tooltipPositioning=#fixed,
~toolTipFor=?,
~tooltipWidthClass="w-fit",
~tooltipForWidthClass="",
~toolTipPosition: option<toolTipPosition>=?,
~customStyle="",
~arrowCustomStyle="",
~textStyleGap="",
~arrowBgClass="",
~bgColor="",
~contentAlign: contentPosition=Middle,
~justifyClass="justify-center",
~flexClass="flex-col",
~height="h-full",
~textStyle="text-fs-11",
~hoverOnToolTip=false,
~tooltipArrowSize=5,
~visibleOnClick=false,
~descriptionComponentClass="flex flex-row-reverse",
~isRelative=true,
~dismissable=false,
) => {
let description = access === AuthTypes.Access ? description : noAccessDescription

<ToolTip
description
descriptionComponent
tooltipPositioning
?toolTipFor
tooltipWidthClass
tooltipForWidthClass
?toolTipPosition
customStyle
arrowCustomStyle
textStyleGap
arrowBgClass
bgColor
contentAlign
justifyClass
flexClass
height
textStyle
hoverOnToolTip
tooltipArrowSize
visibleOnClick
descriptionComponentClass
isRelative
dismissable
/>
}
28 changes: 28 additions & 0 deletions src/components/tooltip/ACLToolTip.resi
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@react.component
let make: (
~access: AuthTypes.authorization,
~noAccessDescription: string=?,
~description: string=?,
~descriptionComponent: React.element=?,
~tooltipPositioning: ToolTip.tooltipPositioning=?,
~toolTipFor: React.element=?,
~tooltipWidthClass: string=?,
~tooltipForWidthClass: string=?,
~toolTipPosition: ToolTip.toolTipPosition=?,
~customStyle: string=?,
~arrowCustomStyle: string=?,
~textStyleGap: string=?,
~arrowBgClass: string=?,
~bgColor: string=?,
~contentAlign: ToolTip.contentPosition=?,
~justifyClass: string=?,
~flexClass: string=?,
~height: string=?,
~textStyle: string=?,
~hoverOnToolTip: bool=?,
~tooltipArrowSize: int=?,
~visibleOnClick: bool=?,
~descriptionComponentClass: string=?,
~isRelative: bool=?,
~dismissable: bool=?,
) => React.element
35 changes: 23 additions & 12 deletions src/screens/HyperSwitch/Connectors/ConnectorList.res
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ module RequestConnector = {
module CantFindProcessor = {
@react.component
let make = (~showRequestConnectorBtn, ~setShowModal) => {
let userPermissionJson = Recoil.useRecoilValueFromAtom(HyperswitchAtom.userPermissionAtom)
let cursorStyles = PermissionUtils.cursorStyles(userPermissionJson.merchantAccountWrite)

<UIUtils.RenderIf condition={showRequestConnectorBtn}>
<div
<ACLDiv
permission=userPermissionJson.merchantAccountWrite
onClick={_ => setShowModal(_ => true)}
className={`text-blue-900 underline underline-offset-4 font-medium`}>
className={`text-blue-900 underline underline-offset-4 font-medium ${cursorStyles}`}>
{"Can't find the processor of your choice?"->React.string}
</div>
</ACLDiv>
</UIUtils.RenderIf>
}
}
Expand Down Expand Up @@ -178,19 +182,26 @@ module NewProcessorCards = {
{connectorList
->Array.mapWithIndex((connector, i) => {
let connectorName = connector->getConnectorNameString
let size = "w-14 h-14 rounded-sm"
<ToolTip
let cursorStyles = PermissionUtils.cursorStyles(
userPermissionJson.merchantConnectorAccountWrite,
)

<ACLDiv
key={i->string_of_int}
permission=userPermissionJson.merchantConnectorAccountWrite
className={`p-2 ${cursorStyles}`}
noAccessDescription=noAccessControlTextForProcessors
tooltipWidthClass="w-30"
description={connectorName->getDisplayNameForConnectors}
toolTipFor={<AddDataAttributes
attributes=[("data-testid", connectorName->String.toLowerCase)]>
onClick={_ => handleClick(connectorName)}>
<AddDataAttributes attributes=[("data-testid", connectorName->String.toLowerCase)]>
<div className="p-2 cursor-pointer" onClick={_ => handleClick(connectorName)}>
<GatewayIcon gateway={connectorName->String.toUpperCase} className=size />
<GatewayIcon
gateway={connectorName->String.toUpperCase} className="w-14 h-14 rounded-sm"
/>
</div>
</AddDataAttributes>}
toolTipPosition={Top}
tooltipWidthClass="w-30"
/>
</AddDataAttributes>
</ACLDiv>
})
->React.array}
</div>
Expand Down
1 change: 1 addition & 0 deletions src/screens/HyperSwitch/Home/HomeUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type resourcesTypes = {
subText: string,
redirectLink: string,
id: string,
access: AuthTypes.authorization,
}

let countries: array<ReactHyperJs.country> = [
Expand Down
60 changes: 42 additions & 18 deletions src/screens/HyperSwitch/Home/HomeV2.res
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,20 @@ module RecipesAndPlugins = {
->QuickStartUtils.getTypedValueFromDict
let isStripePlusPayPalCompleted = enumDetails->checkStripePlusPayPal
let isWooCommercePalCompleted = enumDetails->checkWooCommerce
let userPermissionJson = Recoil.useRecoilValueFromAtom(HyperswitchAtom.userPermissionAtom)
// TODO :: Need to re-evaluate the condition
let blockConditionAccessVal =
userPermissionJson.merchantConnectorAccountRead === NoAccess &&
userPermissionJson.merchantConnectorAccountWrite === NoAccess
? AuthTypes.NoAccess
: AuthTypes.Access

<div className="flex flex-col gap-4">
<p className=headingStyle> {"Recipes & Plugins"->React.string} </p>
<div className="grid grid-cols-1 md:grid-cols-2 w-full gap-4">
<div
<ACLDiv
permission=blockConditionAccessVal
noAccessDescription=noAccessControlText
className={boxCssHover(~ishoverStyleRequired=!isStripePlusPayPalCompleted, ())}
onClick={_ => {
mixpanelEvent(~eventName=`stripe_plus_paypal`, ())
Expand All @@ -250,8 +259,10 @@ module RecipesAndPlugins = {
</p>
<img src="/assets/StripePlusPaypal.svg" className=imageTransitionCss />
</div>
</div>
<div
</ACLDiv>
<ACLDiv
permission=blockConditionAccessVal
noAccessDescription=noAccessControlText
className={boxCssHover(~ishoverStyleRequired=!isWooCommercePalCompleted, ())}
onClick={_ => {
mixpanelEvent(~eventName=`woocommerce`, ())
Expand All @@ -276,7 +287,7 @@ module RecipesAndPlugins = {
</p>
<img src="/assets/Woocommerce.svg" className=imageTransitionCss />
</div>
</div>
</ACLDiv>
</div>
</div>
}
Expand All @@ -286,49 +297,62 @@ module Resources = {
@react.component
let make = () => {
let mixpanelEvent = MixpanelHook.useSendEvent()
let userPermissionJson = Recoil.useRecoilValueFromAtom(HyperswitchAtom.userPermissionAtom)
let elements: array<HomeUtils.resourcesTypes> = [
{
id: "tryTheDemo",
icon: "docs.svg",
headerText: "Try a test payment",
subText: "Experience the Hyperswitch Unified checkout using test credentials",
redirectLink: "",
access: userPermissionJson.paymentWrite,
},
{
id: "openSource",
icon: "blogs.svg",
headerText: "Contribute in open source",
subText: "We welcome all your suggestions, feedbacks, and queries. Hop on to the Open source rail!",
redirectLink: "",
access: Access,
},
{
id: "developerdocs",
icon: "connector.svg",
headerText: "Developer docs",
subText: "Everything you need to know to get to get the SDK up and running resides in here.",
redirectLink: "",
access: Access,
},
]

let onClickHandler = item => {
if item.id === "openSource" {
mixpanelEvent(~eventName=`contribute_in_open_source`, ())
"https://github.com/juspay/hyperswitch"->Window._open
} else if item.id === "developerdocs" {
mixpanelEvent(~eventName=`dev_docs`, ())
"https://hyperswitch.io/docs"->Window._open
} else if item.id === "tryTheDemo" {
RescriptReactRouter.replace("/sdk")
}
}

<>
<div className="flex flex-col gap-4">
<p className=headingStyle> {"Resources"->React.string} </p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{elements
->Array.mapWithIndex((item, index) => {
<div
className="group bg-white border rounded-md p-10 flex flex-col gap-4 group-hover:shadow hover:shadow-homePageBoxShadow cursor-pointer"
let cursorStyles = PermissionUtils.cursorStyles(item.access)
<ACLDiv
key={index->string_of_int}
onClick={_ => {
if item.id === "openSource" {
mixpanelEvent(~eventName=`contribute_in_open_source`, ())
"https://github.com/juspay/hyperswitch"->Window._open
} else if item.id === "developerdocs" {
mixpanelEvent(~eventName=`dev_docs`, ())
"https://hyperswitch.io/docs"->Window._open
} else if item.id === "tryTheDemo" {
RescriptReactRouter.replace("/sdk")
}
}}>
permission=item.access
isRelative=false
contentAlign=Default
tooltipForWidthClass="!h-full"
justifyClass=""
className={`!h-full group bg-white border rounded-md p-10 flex flex-col gap-4 group-hover:shadow hover:shadow-homePageBoxShadow ${cursorStyles}`}
onClick={_ => onClickHandler(item)}>
<img src={`/icons/${item.icon}`} className="h-6 w-6" />
<div className="flex items-center gap-2">
<p className=cardHeaderText> {item.headerText->React.string} </p>
Expand All @@ -339,7 +363,7 @@ module Resources = {
/>
</div>
<p className=paragraphTextVariant> {item.subText->React.string} </p>
</div>
</ACLDiv>
})
->React.array}
</div>
Expand Down
5 changes: 0 additions & 5 deletions src/screens/HyperSwitch/UserManagement/InviteUsers.res
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ module InviteEmailForm = {
let {magicLink} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom
let (roleListData, setRoleListData) = React.useState(_ => [])

let emailList =
ReactFinalForm.useField("emailList").input.value
->getArrayFromJson([])
->Array.joinWithUnsafe(",")
let role =
ReactFinalForm.useField(`roleType`).input.value
->getArrayFromJson([])
Expand Down Expand Up @@ -53,7 +49,6 @@ module InviteEmailForm = {
fieldWrapperClass="w-4/5"
labelClass="!text-black !text-base !-ml-[0.5px]"
/>
<div className="text-sm text-grey-500"> {emailList->React.string} </div>
</div>
<div className="absolute top-10 right-5">
<FormRenderer.SubmitButton text={magicLink ? "Send Invite" : "Add User"} />
Expand Down
2 changes: 2 additions & 0 deletions src/screens/HyperSwitch/UserManagement/PermissionUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,5 @@ let getPermissionJson = permissionList => {
let linkForGetShowLinkViaAccess = (~permission, ~url) => {
permission === Access ? url : ``
}

let cursorStyles = permission => permission === Access ? "cursor-pointer" : "cursor-not-allowed"

0 comments on commit 7c6beec

Please sign in to comment.