Coverage for gws-app/gws/plugin/account/account_action.py: 0%

80 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-17 01:37 +0200

1"""User account action.""" 

2 

3from typing import Optional, cast 

4 

5import gws 

6import gws.config.util 

7import gws.base.action 

8import gws.lib.mime 

9 

10from . import core, helper 

11 

12gws.ext.new.action('account') 

13 

14 

15class Config(gws.base.action.Config): 

16 """User Account action. (added in 8.1)""" 

17 pass 

18 

19 

20class Props(gws.base.action.Props): 

21 pass 

22 

23 

24class MfaProps(gws.Data): 

25 index: int 

26 title: str 

27 qrCode: str 

28 

29 

30class OnboardingStartRequest(gws.Request): 

31 tc: str 

32 

33 

34class OnboardingStartResponse(gws.Response): 

35 tc: str 

36 

37 

38class OnboardingSavePasswordRequest(gws.Request): 

39 tc: str 

40 email: str 

41 password1: str 

42 password2: str 

43 

44 

45class OnboardingSavePasswordResponse(gws.Response): 

46 tc: str 

47 ok: bool 

48 complete: bool 

49 completionUrl: str 

50 mfaList: list[MfaProps] 

51 

52 

53class OnboardingSaveMfaRequest(gws.Request): 

54 tc: str 

55 mfaIndex: Optional[int] 

56 

57 

58class OnboardingSaveMfaResponse(gws.Response): 

59 complete: bool 

60 completionUrl: str 

61 

62 

63class Object(gws.base.action.Object): 

64 h: helper.Object 

65 

66 def configure(self): 

67 self.h = cast(helper.Object, self.root.app.helper('account')) 

68 

69 @gws.ext.command.api('accountOnboardingStart') 

70 def onboarding_start(self, req: gws.WebRequester, p: OnboardingStartRequest) -> OnboardingStartResponse: 

71 account = self.get_account_by_tc(p.tc, core.Category.onboarding, core.Status.new) 

72 self.h.set_status(account, core.Status.onboarding) 

73 return OnboardingStartResponse( 

74 tc=self.h.generate_tc(account, core.Category.onboarding) 

75 ) 

76 

77 @gws.ext.command.api('accountOnboardingSavePassword') 

78 def onboarding_save_password(self, req: gws.WebRequester, p: OnboardingSavePasswordRequest) -> OnboardingSavePasswordResponse: 

79 account = self.get_account_by_tc(p.tc, core.Category.onboarding, core.Status.onboarding) 

80 

81 p1 = p.password1 

82 p2 = p.password2 

83 

84 if account.get('email') != p.email or p1 != p2 or not self.h.validate_password(p1): 

85 return OnboardingSavePasswordResponse( 

86 ok=False, 

87 tc=self.h.generate_tc(account, core.Category.onboarding), 

88 ) 

89 

90 self.h.set_password(account, p1) 

91 

92 mfa = self.h.mfa_options(account) 

93 if mfa: 

94 mfa_secret = self.h.generate_mfa_secret(account) 

95 return OnboardingSavePasswordResponse( 

96 ok=True, 

97 complete=False, 

98 mfaList=self.mfa_props(account, mfa_secret), 

99 tc=self.h.generate_tc(account, core.Category.onboarding), 

100 ) 

101 

102 self.h.set_status(account, core.Status.active) 

103 self.h.clear_tc(account) 

104 return OnboardingSavePasswordResponse( 

105 ok=True, 

106 complete=True, 

107 completionUrl=self.h.onboardingCompletionUrl, 

108 ) 

109 

110 @gws.ext.command.api('accountOnboardingSaveMfa') 

111 def onboarding_save_mfa(self, req: gws.WebRequester, p: OnboardingSaveMfaRequest) -> OnboardingSaveMfaResponse: 

112 account = self.get_account_by_tc(p.tc, core.Category.onboarding, core.Status.onboarding) 

113 

114 self.h.set_mfa(account, p.mfaIndex) 

115 self.h.set_status(account, core.Status.active) 

116 self.h.clear_tc(account) 

117 

118 return OnboardingSaveMfaResponse( 

119 complete=True, 

120 completionUrl=self.h.onboardingCompletionUrl, 

121 ) 

122 

123 ## 

124 

125 def get_account_by_tc(self, tc, category, expected_status): 

126 try: 

127 account = self.h.get_account_by_tc(tc, category, expected_status) 

128 except helper.Error as exc: 

129 raise gws.ForbiddenError() from exc 

130 

131 if not account: 

132 raise gws.ForbiddenError(f'account: {tc=} not found') 

133 

134 return account 

135 

136 def mfa_props(self, account: dict, mfa_secret): 

137 ps = [] 

138 

139 for mo in self.h.mfa_options(account): 

140 ps.append(MfaProps( 

141 index=mo.index, 

142 title=mo.title, 

143 qrCode=self.h.qr_code_for_mfa(account, mo, mfa_secret) 

144 )) 

145 

146 return ps