fix: mask email and phone number on the backend (#563)

* fix: mask email and phone number on the backend

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: login with masked email or phone

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: improve regex

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
This commit is contained in:
Yixiang Zhao
2022-03-15 12:54:57 +08:00
committed by GitHub
parent 377e200837
commit 9f7924a6e0
7 changed files with 67 additions and 17 deletions

View File

@ -167,9 +167,16 @@ func (c *ApiController) Login() {
var verificationCodeType string var verificationCodeType string
var checkResult string var checkResult string
if form.Name != "" {
user = object.GetUserByFields(form.Organization, form.Name)
}
// check result through Email or Phone // check result through Email or Phone
if strings.Contains(form.Username, "@") { if strings.Contains(form.Username, "@") {
verificationCodeType = "email" verificationCodeType = "email"
if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
form.Username = user.Email
}
checkResult = object.CheckVerificationCode(form.Username, form.Code) checkResult = object.CheckVerificationCode(form.Username, form.Code)
} else { } else {
verificationCodeType = "phone" verificationCodeType = "phone"
@ -178,6 +185,9 @@ func (c *ApiController) Login() {
c.ResponseError(responseText) c.ResponseError(responseText)
return return
} }
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
form.Username = user.Phone
}
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username) checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
checkResult = object.CheckVerificationCode(checkPhone, form.Code) checkResult = object.CheckVerificationCode(checkPhone, form.Code)
} }

View File

@ -194,15 +194,19 @@ func (c *ApiController) GetEmailAndPhone() {
return return
} }
respUser := object.User{Email: user.Email, Phone: user.Phone, Name: user.Name} respUser := object.User{Name: user.Name}
var contentType string var contentType string
switch form.Username { switch form.Username {
case user.Email: case user.Email:
contentType = "email" contentType = "email"
respUser.Email = user.Email
case user.Phone: case user.Phone:
contentType = "phone" contentType = "phone"
respUser.Phone = user.Phone
case user.Name: case user.Name:
contentType = "username" contentType = "username"
respUser.Email = util.GetMaskedEmail(user.Email)
respUser.Phone = util.GetMaskedPhone(user.Phone)
} }
c.ResponseOk(respUser, contentType) c.ResponseOk(respUser, contentType)

View File

@ -74,8 +74,16 @@ func (c *ApiController) SendVerificationCode() {
} }
sendResp := errors.New("Invalid dest type") sendResp := errors.New("Invalid dest type")
if user == nil && checkUser != "" && checkUser != "true" {
_, name := util.GetOwnerAndNameFromId(orgId)
user = object.GetUser(fmt.Sprintf("%s/%s", name, checkUser))
}
switch destType { switch destType {
case "email": case "email":
if user != nil && util.GetMaskedEmail(user.Email) == dest {
dest = user.Email
}
if !util.IsEmailValid(dest) { if !util.IsEmailValid(dest) {
c.ResponseError("Invalid Email address") c.ResponseError("Invalid Email address")
return return
@ -84,6 +92,9 @@ func (c *ApiController) SendVerificationCode() {
provider := application.GetEmailProvider() provider := application.GetEmailProvider()
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest) sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
case "phone": case "phone":
if user != nil && util.GetMaskedPhone(user.Phone) == dest {
dest = user.Phone
}
if !util.IsPhoneCnValid(dest) { if !util.IsPhoneCnValid(dest) {
c.ResponseError("Invalid phone number") c.ResponseError("Invalid phone number")
return return

View File

@ -20,10 +20,12 @@ import (
) )
var rePhoneCn *regexp.Regexp var rePhoneCn *regexp.Regexp
var rePhone *regexp.Regexp
func init() { func init() {
// https://learnku.com/articles/31543 // https://learnku.com/articles/31543
rePhoneCn, _ = regexp.Compile(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`) rePhoneCn, _ = regexp.Compile(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`)
rePhone, _ = regexp.Compile("(\\d{3})\\d*(\\d{4})")
} }
func IsEmailValid(email string) bool { func IsEmailValid(email string) bool {
@ -34,3 +36,7 @@ func IsEmailValid(email string) bool {
func IsPhoneCnValid(phone string) bool { func IsPhoneCnValid(phone string) bool {
return rePhoneCn.MatchString(phone) return rePhoneCn.MatchString(phone)
} }
func getMaskedPhone(phone string) string {
return rePhone.ReplaceAllString(phone, "$1****$2")
}

View File

@ -206,3 +206,28 @@ func IsChinese(str string) bool {
} }
return flag return flag
} }
func GetMaskedPhone(phone string) string {
return getMaskedPhone(phone)
}
func GetMaskedEmail(email string) string {
if email == "" {
return ""
}
tokens := strings.Split(email, "@")
username := maskString(tokens[0])
domain := tokens[1]
domainTokens := strings.Split(domain, ".")
domainTokens[len(domainTokens) - 2] = maskString(domainTokens[len(domainTokens) - 2])
return fmt.Sprintf("%s@%s", username, strings.Join(domainTokens, "."))
}
func maskString(str string) string {
if len(str) <= 2 {
return str
} else {
return fmt.Sprintf("%c%s%c", str[0], strings.Repeat("*", len(str) - 2), str[len(str) - 1])
}
}

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Col, Form, Select, Input, Row, Steps} from "antd"; import {Button, Col, Form, Input, Row, Select, Steps} from "antd";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import * as ApplicationBackend from "../backend/ApplicationBackend"; import * as ApplicationBackend from "../backend/ApplicationBackend";
import * as Util from "./Util"; import * as Util from "./Util";
@ -43,6 +43,7 @@ class ForgetPage extends React.Component {
msg: null, msg: null,
userId: "", userId: "",
username: "", username: "",
name: "",
email: "", email: "",
isFixed: false, isFixed: false,
fixedContent: "", fixedContent: "",
@ -100,7 +101,7 @@ class ForgetPage extends React.Component {
if (res.status === "ok") { if (res.status === "ok") {
const phone = res.data.phone; const phone = res.data.phone;
const email = res.data.email; const email = res.data.email;
this.setState({phone: phone, email: email, username: res.data.name}); this.setState({phone: phone, email: email, username: res.data.name, name: res.data.name});
if (phone !== "" && email === "") { if (phone !== "" && email === "") {
this.setState({ this.setState({
@ -134,15 +135,16 @@ class ForgetPage extends React.Component {
break; break;
case "step2": case "step2":
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
if(this.state.verifyType=="email"){ if (this.state.verifyType === "email") {
this.setState({username: this.state.email}) this.setState({username: this.state.email})
}else if(this.state.verifyType=="phone"){ } else if (this.state.verifyType === "phone") {
this.setState({username: this.state.phone}) this.setState({username: this.state.phone})
} }
AuthBackend.login({ AuthBackend.login({
application: forms.step2.getFieldValue("application"), application: forms.step2.getFieldValue("application"),
organization: forms.step2.getFieldValue("organization"), organization: forms.step2.getFieldValue("organization"),
username: this.state.username, username: this.state.username,
name: this.state.name,
code: forms.step2.getFieldValue("emailCode"), code: forms.step2.getFieldValue("emailCode"),
phonePrefix: this.state.application?.organizationObj.phonePrefix, phonePrefix: this.state.application?.organizationObj.phonePrefix,
type: "login" type: "login"
@ -179,7 +181,7 @@ class ForgetPage extends React.Component {
if (this.state.phone !== "") { if (this.state.phone !== "") {
options.push( options.push(
<Option key={"phone"} value={"phone"}> <Option key={"phone"} value={"phone"}>
&nbsp;&nbsp;{Setting.getMaskedPhone(this.state.phone)} &nbsp;&nbsp;{this.state.phone}
</Option> </Option>
); );
} }
@ -187,7 +189,7 @@ class ForgetPage extends React.Component {
if (this.state.email !== "") { if (this.state.email !== "") {
options.push( options.push(
<Option key={"email"} value={"email"}> <Option key={"email"} value={"email"}>
&nbsp;&nbsp;{Setting.getMaskedEmail(this.state.email)} &nbsp;&nbsp;{this.state.email}
</Option> </Option>
); );
} }
@ -349,12 +351,12 @@ class ForgetPage extends React.Component {
{this.state.verifyType === "email" ? ( {this.state.verifyType === "email" ? (
<CountDownInput <CountDownInput
disabled={this.state.username === "" || this.state.verifyType === ""} disabled={this.state.username === "" || this.state.verifyType === ""}
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationOrgName(this.state.application)]} onButtonClickArgs={[this.state.email, "email", Setting.getApplicationOrgName(this.state.application), this.state.name]}
/> />
) : ( ) : (
<CountDownInput <CountDownInput
disabled={this.state.username === "" || this.state.verifyType === ""} disabled={this.state.username === "" || this.state.verifyType === ""}
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationOrgName(this.state.application)]} onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationOrgName(this.state.application), this.state.name]}
/> />
)} )}
</Form.Item> </Form.Item>

View File

@ -49,14 +49,6 @@ export const CountDownInput = (props) => {
const handleOk = () => { const handleOk = () => {
setVisible(false); setVisible(false);
if (isValidEmail(onButtonClickArgs[0])) {
onButtonClickArgs[1] = "email";
} else if (isValidPhone(onButtonClickArgs[0])) {
onButtonClickArgs[1] = "phone";
} else {
Util.showMessage("error", i18next.t("login:Invalid Email or phone"))
return;
}
setButtonLoading(true) setButtonLoading(true)
UserBackend.sendCode(checkType, checkId, key, ...onButtonClickArgs).then(res => { UserBackend.sendCode(checkType, checkId, key, ...onButtonClickArgs).then(res => {
setKey(""); setKey("");