|
4 | 4 |
|
5 | 5 | use Illuminate\Support\Collection; |
6 | 6 | use Statamic\Contracts\Auth\Role; |
| 7 | +use Statamic\Facades\TwoFactor; |
7 | 8 | use Statamic\Facades\URL; |
8 | 9 | use Statamic\Facades\User; |
9 | 10 | use Statamic\Fields\Field; |
@@ -795,6 +796,318 @@ public function eventUrl($url, $relative = false) |
795 | 796 | ); |
796 | 797 | } |
797 | 798 |
|
| 799 | + /** |
| 800 | + * Output a boolean of whether two-factor auth is enabled for the user. |
| 801 | + * |
| 802 | + * Maps to {{ user:two_factor_enabled }} |
| 803 | + */ |
| 804 | + public function twoFactorEnabled(): bool |
| 805 | + { |
| 806 | + return (bool) User::current()?->hasEnabledTwoFactorAuthentication(); |
| 807 | + } |
| 808 | + |
| 809 | + /** |
| 810 | + * Output a two-factor challenge form for login verification. |
| 811 | + * |
| 812 | + * Maps to {{ user:two_factor_challenge_form }} |
| 813 | + * |
| 814 | + * @return string|array |
| 815 | + */ |
| 816 | + public function twoFactorChallengeForm() |
| 817 | + { |
| 818 | + if ( |
| 819 | + ! TwoFactor::enabled() |
| 820 | + || session()->missing('login.id') |
| 821 | + ) { |
| 822 | + return; |
| 823 | + } |
| 824 | + |
| 825 | + $params = []; |
| 826 | + |
| 827 | + $data = $this->getFormSession(); |
| 828 | + |
| 829 | + $knownParams = ['redirect', 'error_redirect', 'allow_request_redirect']; |
| 830 | + |
| 831 | + $method = 'POST'; |
| 832 | + $action = route('statamic.two-factor-challenge'); |
| 833 | + |
| 834 | + if ($redirect = $this->getRedirectUrl()) { |
| 835 | + $params['redirect'] = $this->parseRedirect($redirect); |
| 836 | + } |
| 837 | + |
| 838 | + if ($errorRedirect = $this->getErrorRedirectUrl()) { |
| 839 | + $params['error_redirect'] = $this->parseRedirect($errorRedirect); |
| 840 | + } |
| 841 | + |
| 842 | + if (! $this->canParseContents()) { |
| 843 | + return array_merge([ |
| 844 | + 'attrs' => $this->formAttrs($action, $method, $knownParams), |
| 845 | + 'params' => $this->formMetaPrefix($this->formParams($method, $params)), |
| 846 | + ], $data); |
| 847 | + } |
| 848 | + |
| 849 | + $html = $this->formOpen($action, $method, $knownParams); |
| 850 | + |
| 851 | + $html .= $this->formMetaFields($params); |
| 852 | + |
| 853 | + $html .= $this->parse($data); |
| 854 | + |
| 855 | + $html .= $this->formClose(); |
| 856 | + |
| 857 | + return $html; |
| 858 | + } |
| 859 | + |
| 860 | + /** |
| 861 | + * Output a two-factor enable form. |
| 862 | + * |
| 863 | + * Maps to {{ user:two_factor_enable_form }} |
| 864 | + * |
| 865 | + * @return string|array |
| 866 | + */ |
| 867 | + public function twoFactorEnableForm() |
| 868 | + { |
| 869 | + $user = User::current(); |
| 870 | + |
| 871 | + if ( |
| 872 | + ! TwoFactor::enabled() |
| 873 | + || ! $user |
| 874 | + || $user->hasEnabledTwoFactorAuthentication() |
| 875 | + ) { |
| 876 | + return; |
| 877 | + } |
| 878 | + |
| 879 | + $params = []; |
| 880 | + |
| 881 | + $data = $this->getFormSession('user.two_factor_enable'); |
| 882 | + |
| 883 | + $knownParams = ['redirect', 'allow_request_redirect']; |
| 884 | + |
| 885 | + $method = 'POST'; |
| 886 | + $action = route('statamic.users.two-factor.enable'); |
| 887 | + |
| 888 | + if ($redirect = $this->getRedirectUrl()) { |
| 889 | + $params['redirect'] = $this->parseRedirect($redirect); |
| 890 | + } |
| 891 | + |
| 892 | + if (! $this->canParseContents()) { |
| 893 | + return array_merge([ |
| 894 | + 'attrs' => $this->formAttrs($action, $method, $knownParams), |
| 895 | + 'params' => $this->formMetaPrefix($this->formParams($method, $params)), |
| 896 | + ], $data); |
| 897 | + } |
| 898 | + |
| 899 | + $html = $this->formOpen($action, $method, $knownParams); |
| 900 | + |
| 901 | + $html .= $this->formMetaFields($params); |
| 902 | + |
| 903 | + $html .= $this->parse($data); |
| 904 | + |
| 905 | + $html .= $this->formClose(); |
| 906 | + |
| 907 | + return $html; |
| 908 | + } |
| 909 | + |
| 910 | + /** |
| 911 | + * Output a two-factor setup form. |
| 912 | + * |
| 913 | + * Maps to {{ user:two_factor_setup_form }} |
| 914 | + * |
| 915 | + * @return string|array |
| 916 | + */ |
| 917 | + public function twoFactorSetupForm() |
| 918 | + { |
| 919 | + $user = User::current(); |
| 920 | + |
| 921 | + if ( |
| 922 | + ! TwoFactor::enabled() |
| 923 | + || ! $user |
| 924 | + || $user->hasEnabledTwoFactorAuthentication() |
| 925 | + || empty($user->two_factor_secret) |
| 926 | + ) { |
| 927 | + return; |
| 928 | + } |
| 929 | + |
| 930 | + $params = []; |
| 931 | + |
| 932 | + $data = $this->getFormSession('user.two_factor_setup'); |
| 933 | + |
| 934 | + $data['qr_code'] = $user->twoFactorQrCodeSvg(); |
| 935 | + $data['qr_code_url'] = 'data:image/svg+xml;base64,'.base64_encode($user->twoFactorQrCodeSvg()); |
| 936 | + $data['secret_key'] = $user->twoFactorSecretKey(); |
| 937 | + |
| 938 | + $knownParams = ['redirect', 'error_redirect', 'allow_request_redirect']; |
| 939 | + |
| 940 | + $method = 'POST'; |
| 941 | + $action = route('statamic.users.two-factor.confirm'); |
| 942 | + |
| 943 | + if ($redirect = $this->getRedirectUrl()) { |
| 944 | + $params['redirect'] = $this->parseRedirect($redirect); |
| 945 | + } |
| 946 | + |
| 947 | + if ($errorRedirect = $this->getErrorRedirectUrl()) { |
| 948 | + $params['error_redirect'] = $this->parseRedirect($errorRedirect); |
| 949 | + } |
| 950 | + |
| 951 | + if (! $this->canParseContents()) { |
| 952 | + return array_merge([ |
| 953 | + 'attrs' => $this->formAttrs($action, $method, $knownParams), |
| 954 | + 'params' => $this->formMetaPrefix($this->formParams($method, $params)), |
| 955 | + ], $data); |
| 956 | + } |
| 957 | + |
| 958 | + $html = $this->formOpen($action, $method, $knownParams); |
| 959 | + |
| 960 | + $html .= $this->formMetaFields($params); |
| 961 | + |
| 962 | + $html .= $this->parse($data); |
| 963 | + |
| 964 | + $html .= $this->formClose(); |
| 965 | + |
| 966 | + return $html; |
| 967 | + } |
| 968 | + |
| 969 | + /** |
| 970 | + * Output the user's two-factor recovery codes. |
| 971 | + * |
| 972 | + * Maps to {{ user:two_factor_recovery_codes }} |
| 973 | + * |
| 974 | + * @return array|string |
| 975 | + */ |
| 976 | + public function twoFactorRecoveryCodes() |
| 977 | + { |
| 978 | + $user = User::current(); |
| 979 | + |
| 980 | + if ( |
| 981 | + ! TwoFactor::enabled() |
| 982 | + || ! $user?->hasEnabledTwoFactorAuthentication() |
| 983 | + ) { |
| 984 | + return $this->parser ? null : []; |
| 985 | + } |
| 986 | + |
| 987 | + $codes = collect($user->twoFactorRecoveryCodes())->map(fn ($code) => ['code' => $code]); |
| 988 | + |
| 989 | + return $this->parser ? $this->parseLoop($codes) : $codes->all(); |
| 990 | + } |
| 991 | + |
| 992 | + /** |
| 993 | + * Outputs a URL to download two-factor recovery codes. |
| 994 | + * |
| 995 | + * Maps to {{ user:two_factor_recovery_codes_download_url }} |
| 996 | + * |
| 997 | + * @return string |
| 998 | + */ |
| 999 | + public function twoFactorRecoveryCodesDownloadUrl() |
| 1000 | + { |
| 1001 | + $user = User::current(); |
| 1002 | + |
| 1003 | + if ( |
| 1004 | + ! TwoFactor::enabled() |
| 1005 | + || ! $user?->hasEnabledTwoFactorAuthentication() |
| 1006 | + ) { |
| 1007 | + return; |
| 1008 | + } |
| 1009 | + |
| 1010 | + return route('statamic.users.two-factor.recovery-codes.download'); |
| 1011 | + } |
| 1012 | + |
| 1013 | + /** |
| 1014 | + * Output a form to regenerate two-factor recovery codes. |
| 1015 | + * |
| 1016 | + * Maps to {{ user:reset_two_factor_recovery_codes_form }} |
| 1017 | + * |
| 1018 | + * @return string|array |
| 1019 | + */ |
| 1020 | + public function resetTwoFactorRecoveryCodesForm() |
| 1021 | + { |
| 1022 | + $user = User::current(); |
| 1023 | + |
| 1024 | + if ( |
| 1025 | + ! TwoFactor::enabled() |
| 1026 | + || ! $user?->hasEnabledTwoFactorAuthentication() |
| 1027 | + ) { |
| 1028 | + return; |
| 1029 | + } |
| 1030 | + |
| 1031 | + $params = []; |
| 1032 | + |
| 1033 | + $data = $this->getFormSession('user.two_factor_reset_recovery_codes'); |
| 1034 | + |
| 1035 | + $knownParams = ['redirect', 'allow_request_redirect']; |
| 1036 | + |
| 1037 | + $method = 'POST'; |
| 1038 | + $action = route('statamic.users.two-factor.recovery-codes.generate'); |
| 1039 | + |
| 1040 | + if ($redirect = $this->getRedirectUrl()) { |
| 1041 | + $params['redirect'] = $this->parseRedirect($redirect); |
| 1042 | + } |
| 1043 | + |
| 1044 | + if (! $this->canParseContents()) { |
| 1045 | + return array_merge([ |
| 1046 | + 'attrs' => $this->formAttrs($action, $method, $knownParams), |
| 1047 | + 'params' => $this->formMetaPrefix($this->formParams($method, $params)), |
| 1048 | + ], $data); |
| 1049 | + } |
| 1050 | + |
| 1051 | + $html = $this->formOpen($action, $method, $knownParams); |
| 1052 | + |
| 1053 | + $html .= $this->formMetaFields($params); |
| 1054 | + |
| 1055 | + $html .= $this->parse($data); |
| 1056 | + |
| 1057 | + $html .= $this->formClose(); |
| 1058 | + |
| 1059 | + return $html; |
| 1060 | + } |
| 1061 | + |
| 1062 | + /** |
| 1063 | + * Output a form to disable two-factor authentication. |
| 1064 | + * |
| 1065 | + * Maps to {{ user:disable_two_factor_form }} |
| 1066 | + * |
| 1067 | + * @return string|array |
| 1068 | + */ |
| 1069 | + public function disableTwoFactorForm() |
| 1070 | + { |
| 1071 | + $user = User::current(); |
| 1072 | + |
| 1073 | + if ( |
| 1074 | + ! TwoFactor::enabled() |
| 1075 | + || ! $user?->hasEnabledTwoFactorAuthentication() |
| 1076 | + ) { |
| 1077 | + return; |
| 1078 | + } |
| 1079 | + |
| 1080 | + $params = []; |
| 1081 | + |
| 1082 | + $data = $this->getFormSession('user.two_factor_disable'); |
| 1083 | + |
| 1084 | + $knownParams = ['redirect', 'allow_request_redirect']; |
| 1085 | + |
| 1086 | + $method = 'DELETE'; |
| 1087 | + $action = route('statamic.users.two-factor.disable'); |
| 1088 | + |
| 1089 | + if ($redirect = $this->getRedirectUrl()) { |
| 1090 | + $params['redirect'] = $this->parseRedirect($redirect); |
| 1091 | + } |
| 1092 | + |
| 1093 | + if (! $this->canParseContents()) { |
| 1094 | + return array_merge([ |
| 1095 | + 'attrs' => $this->formAttrs($action, $method, $knownParams), |
| 1096 | + 'params' => $this->formMetaPrefix($this->formParams($method, $params)), |
| 1097 | + ], $data); |
| 1098 | + } |
| 1099 | + |
| 1100 | + $html = $this->formOpen($action, $method, $knownParams); |
| 1101 | + |
| 1102 | + $html .= $this->formMetaFields($params); |
| 1103 | + |
| 1104 | + $html .= $this->parse($data); |
| 1105 | + |
| 1106 | + $html .= $this->formClose(); |
| 1107 | + |
| 1108 | + return $html; |
| 1109 | + } |
| 1110 | + |
798 | 1111 | /** |
799 | 1112 | * Get the redirect URL. |
800 | 1113 | * |
|
0 commit comments