diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/SignPrincipal.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/SignPrincipal.java index 8faaf9861..fff010f85 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/SignPrincipal.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/SignPrincipal.java @@ -37,6 +37,8 @@ public class SignPrincipal implements UserDetails { String sessionId; + int twoFactor; + List grantedAuthority; List grantedAuthorityApps; @@ -204,7 +206,19 @@ public class SignPrincipal implements UserDetails { } } - @Override + public int getTwoFactor() { + return twoFactor; + } + + public void setTwoFactor(int twoFactor) { + this.twoFactor = twoFactor; + } + + public void clearTwoFactor() { + this.twoFactor = 0; + } + + @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Principal [username="); diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwt.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwt.java index a9094466f..a7bd1c522 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwt.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwt.java @@ -104,7 +104,7 @@ public class AuthJwt implements Serializable { this.email = principal.getUserInfo().getEmail(); this.instId = principal.getUserInfo().getInstId(); this.instName = principal.getUserInfo().getInstName(); - + this.twoFactor =principal.getTwoFactor(); this.authorities = new ArrayList<>(); for(GrantedAuthority grantedAuthority :authentication.getAuthorities()) { this.authorities.add(grantedAuthority.getAuthority()); diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwtService.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwtService.java index da9fb1bac..98df61db3 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwtService.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/jwt/AuthJwtService.java @@ -22,6 +22,7 @@ import java.util.Date; import org.apache.commons.lang3.StringUtils; import org.dromara.maxkey.authn.SignPrincipal; +import org.dromara.maxkey.constants.ConstsJwt; import org.dromara.maxkey.crypto.jwt.Hmac512Service; import org.dromara.maxkey.entity.idm.UserInfo; import org.dromara.maxkey.web.WebContext; @@ -61,7 +62,8 @@ public class AuthJwtService { .expirationTime(expirationTime) .claim("locale", userInfo.getLocale()) .claim("kid", Hmac512Service.MXK_AUTH_JWK) - .claim("institution", userInfo.getInstId()) + .claim(ConstsJwt.USER_ID, userInfo.getId()) + .claim(ConstsJwt.INST_ID, userInfo.getInstId()) .build(); return signedJWT(jwtClaims); diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java index 975926529..6b44aac02 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java @@ -41,4 +41,10 @@ public interface SessionManager { public void terminate(String sessionId,String userId,String username); public void visited(String sessionId , VisitedDto visited); + + public void createTwoFactor(String sessionId, Session session); + + public Session removeTwoFactor(String sessionId); + + public Session getTwoFactor(String sessionId); } diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/InMemorySessionManager.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/InMemorySessionManager.java index 0836b4611..4179df6c0 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/InMemorySessionManager.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/InMemorySessionManager.java @@ -38,21 +38,30 @@ public class InMemorySessionManager implements SessionManager{ static final long CACHE_MAXIMUM_SIZE = 2000000; protected int validitySeconds = 60 * 30; //default 30 minutes. - protected static Cache sessionStore = - Caffeine.newBuilder() - .expireAfterWrite(10, TimeUnit.MINUTES) - .maximumSize(CACHE_MAXIMUM_SIZE) - .build(); + Cache sessionStore; + + Cache sessionTwoFactorStore; public InMemorySessionManager(int validitySeconds) { super(); this.validitySeconds = validitySeconds; + if(validitySeconds > 0) { sessionStore = Caffeine.newBuilder() .expireAfterWrite(validitySeconds, TimeUnit.SECONDS) .maximumSize(CACHE_MAXIMUM_SIZE) .build(); + }else { + sessionStore = Caffeine.newBuilder() + .expireAfterWrite(10, TimeUnit.MINUTES) + .maximumSize(CACHE_MAXIMUM_SIZE) + .build(); + } + sessionTwoFactorStore = Caffeine.newBuilder() + .expireAfterWrite(10, TimeUnit.MINUTES) + .maximumSize(CACHE_MAXIMUM_SIZE) + .build(); } @Override @@ -127,5 +136,24 @@ public class InMemorySessionManager implements SessionManager{ _logger.debug("session {} store visited {} ." , sessionId , visited); } } + + @Override + public void createTwoFactor(String sessionId, Session session) { + session.setExpiredTime(session.getLastAccessTime().plusSeconds(validitySeconds)); + sessionTwoFactorStore.put(sessionId, session); + } + + @Override + public Session removeTwoFactor(String sessionId) { + Session session = sessionTwoFactorStore.getIfPresent(sessionId); + sessionTwoFactorStore.invalidate(sessionId); + return session; + } + + @Override + public Session getTwoFactor(String sessionId) { + Session session = sessionTwoFactorStore.getIfPresent(sessionId); + return session; + } } diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/RedisSessionManager.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/RedisSessionManager.java index e4dc4f6b3..6245bcf28 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/RedisSessionManager.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/RedisSessionManager.java @@ -35,9 +35,14 @@ public class RedisSessionManager implements SessionManager { protected int validitySeconds = 60 * 30; //default 30 minutes. + int twoFactorValidity = 10 * 60; //default 10 minutes. + RedisConnectionFactory connectionFactory; - public static String PREFIX="MXK_SESSION_"; + public static final String PREFIX = "MXK_SESSION_"; + + public static final String PREFIX_TWOFACTOR = "mxk:session:twofactor:%s"; + public String getKey(String sessionId) { return PREFIX + sessionId; @@ -150,4 +155,34 @@ public class RedisSessionManager implements SessionManager { } } + public String formatTwoFactorKey(String sessionId) { + return PREFIX_TWOFACTOR.formatted(sessionId) ; + } + + @Override + public void createTwoFactor(String sessionId, Session session) { + session.setExpiredTime(session.getLastAccessTime().plusSeconds(validitySeconds)); + RedisConnection conn = connectionFactory.getConnection(); + conn.setexObject( formatTwoFactorKey(sessionId), twoFactorValidity, session); + conn.close(); + + } + + @Override + public Session removeTwoFactor(String sessionId) { + RedisConnection conn = connectionFactory.getConnection(); + Session ticket = conn.getObject(formatTwoFactorKey(sessionId)); + conn.delete(formatTwoFactorKey(sessionId)); + conn.close(); + return ticket; + } + + @Override + public Session getTwoFactor(String sessionId) { + RedisConnection conn = connectionFactory.getConnection(); + Session session = conn.getObject(formatTwoFactorKey(sessionId)); + conn.close(); + return session; + } + } diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/SessionManagerImpl.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/SessionManagerImpl.java index 10bbc8edb..cad5252de 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/SessionManagerImpl.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/impl/SessionManagerImpl.java @@ -207,4 +207,35 @@ public class SessionManagerImpl implements SessionManager{ redisSessionManager.visited(sessionId,visited); } } + + @Override + public void createTwoFactor(String sessionId, Session session) { + if(isRedis) { + redisSessionManager.createTwoFactor(sessionId, session); + }else { + inMemorySessionManager.createTwoFactor(sessionId, session); + } + } + + @Override + public Session removeTwoFactor(String sessionId) { + Session session = null; + if(isRedis) { + session = redisSessionManager.removeTwoFactor(sessionId); + }else { + session = inMemorySessionManager.removeTwoFactor(sessionId); + } + return session; + } + + @Override + public Session getTwoFactor(String sessionId) { + Session session = null; + if(isRedis) { + session = redisSessionManager.getTwoFactor(sessionId); + }else { + session = inMemorySessionManager.getTwoFactor(sessionId); + } + return session; + } } diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java index 1d8298da5..01a349200 100644 --- a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java @@ -20,6 +20,7 @@ package org.dromara.maxkey.authn.provider; import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.dromara.maxkey.authn.LoginCredential; import org.dromara.maxkey.authn.SignPrincipal; import org.dromara.maxkey.authn.jwt.AuthTokenService; @@ -30,6 +31,7 @@ import org.dromara.maxkey.authn.web.AuthorizationUtils; import org.dromara.maxkey.configuration.ApplicationConfig; import org.dromara.maxkey.constants.ConstsLoginType; import org.dromara.maxkey.constants.ConstsStatus; +import org.dromara.maxkey.constants.ConstsTwoFactor; import org.dromara.maxkey.entity.idm.UserInfo; import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn; import org.dromara.maxkey.password.onetimepwd.MailOtpAuthnService; @@ -93,6 +95,10 @@ public abstract class AbstractAuthenticationProvider { public abstract Authentication doAuthenticate(LoginCredential authentication); + public Authentication doTwoFactorAuthenticate(LoginCredential credential , UserInfo user) { + return null; + } + @SuppressWarnings("rawtypes") public boolean supports(Class authentication) { return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); @@ -147,6 +153,13 @@ public abstract class AbstractAuthenticationProvider { */ session.setAuthentication(authenticationToken); + if(credential.getAuthType().equalsIgnoreCase(AuthType.NORMAL) + && userInfo.getAuthnType() > ConstsTwoFactor.NONE ) { + //用户配置二次认证 + principal.setTwoFactor(userInfo.getAuthnType()); + this.sessionManager.createTwoFactor(session.getId(), session); + } + //create session this.sessionManager.create(session.getId(), session); @@ -258,5 +271,18 @@ public abstract class AbstractAuthenticationProvider { } return true; } + + /** + * check input otp empty. + * + * @param password String + * @return + */ + protected boolean emptyOtpCaptchaValid(String otpCaptcha) { + if (StringUtils.isBlank(otpCaptcha)) { + throw new BadCredentialsException(WebContext.getI18nValue("login.error.otpCaptcha.null")); + } + return true; + } } diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/TwoFactorAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/TwoFactorAuthenticationProvider.java new file mode 100644 index 000000000..f97fddf45 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/TwoFactorAuthenticationProvider.java @@ -0,0 +1,127 @@ + + + + +package org.dromara.maxkey.authn.provider.twofactor; + +import java.util.HashMap; +import java.util.Map; + +import org.dromara.maxkey.authn.LoginCredential; +import org.dromara.maxkey.authn.SignPrincipal; +import org.dromara.maxkey.authn.jwt.AuthTokenService; +import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; +import org.dromara.maxkey.authn.realm.AbstractAuthenticationRealm; +import org.dromara.maxkey.authn.session.Session; +import org.dromara.maxkey.authn.session.SessionManager; +import org.dromara.maxkey.authn.web.AuthorizationUtils; +import org.dromara.maxkey.constants.ConstsJwt; +import org.dromara.maxkey.constants.ConstsLoginType; +import org.dromara.maxkey.constants.ConstsTwoFactor; +import org.dromara.maxkey.entity.idm.UserInfo; +import org.dromara.maxkey.persistence.service.LoginService; +import org.dromara.maxkey.web.WebConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import com.nimbusds.jwt.JWTClaimsSet; + +/** + * TwoFactor Authentication provider.双因素认证提供者 + * + * @author Crystal.Sea + * + */ +public class TwoFactorAuthenticationProvider extends AbstractAuthenticationProvider { + private static final Logger logger =LoggerFactory.getLogger(TwoFactorAuthenticationProvider.class); + + Map twoFactorProvider = new HashMap<>(); + + public String getProviderName() { + return "twoFactor" + PROVIDER_SUFFIX; + } + + public TwoFactorAuthenticationProvider( + AbstractAuthenticationRealm authenticationRealm, + SessionManager sessionManager, + LoginService loginService, + AuthTokenService authTokenService) { + this.authenticationRealm = authenticationRealm; + this.sessionManager = sessionManager; + this.authTokenService = authTokenService; + } + + public void addProvider(int twoFactor,AbstractAuthenticationProvider provider) { + twoFactorProvider.put(twoFactor+"", provider); + } + + @Override + public Authentication doAuthenticate(LoginCredential credential) { + logger.debug("Credential {}" , credential); + emptyOtpCaptchaValid(credential.getOtpCaptcha()); + try { + if(authTokenService.validateJwtToken(credential.getJwtToken())) { + //解析refreshToken,转换会话id + JWTClaimsSet claim = authTokenService.resolve(credential.getJwtToken()); + String sessionId = claim.getJWTID(); + String userId = claim.getClaim(ConstsJwt.USER_ID).toString(); + //String style = claim.getClaim(AuthorizationUtils.STYLE).toString(); + //尝试刷新会话 + logger.trace("Try to get user {} , sessionId [{}]" , userId, sessionId); + Session session = sessionManager.getTwoFactor(sessionId); + if(session != null) {//有会话 + Authentication twoFactorAuth = null; + SignPrincipal principal =(SignPrincipal) session.getAuthentication().getPrincipal(); + String loginType; + switch(principal.getTwoFactor()) { + case ConstsTwoFactor.TOTP -> { + loginType = ConstsLoginType.TwoFactor.TWO_FACTOR_TOTP; + } + case ConstsTwoFactor.EMAIL -> { + loginType = ConstsLoginType.TwoFactor.TWO_FACTOR_EMAIL; + } + case ConstsTwoFactor.SMS -> { + loginType = ConstsLoginType.TwoFactor.TWO_FACTOR_MOBILE; + } + default ->{ + loginType = ConstsLoginType.TwoFactor.TWO_FACTOR_TOTP; + } + } + logger.debug("loginType {}",loginType); + AbstractAuthenticationProvider authenticationProvider = twoFactorProvider.get(principal.getTwoFactor()+""); + logger.debug("Provider {}",authenticationProvider.getProviderName()); + UserInfo user = authenticationRealm.loadUserInfoById(userId); + //进行二次认证校验 + twoFactorAuth = authenticationProvider.doTwoFactorAuthenticate(credential , user); + + if(twoFactorAuth != null) { + logger.debug("twoFactorAuth success ."); + //设置正常状态 + principal.clearTwoFactor(); + //重新设置令牌参数 + sessionManager.create(sessionId, session); + sessionManager.removeTwoFactor(sessionId); + AuthorizationUtils.setAuthentication(session.getAuthentication()); + authenticationRealm.insertLoginHistory(user, + loginType, + "", + "xe00000004", + WebConstants.LOGIN_RESULT.SUCCESS); + return session.getAuthentication(); + }else { + logger.debug("twoFactorAuth fail ."); + } + }else {//无会话 + logger.debug("Session is timeout , sessionId [{}]" , sessionId); + } + }else {//验证失效 + logger.debug("jwt token is not validate ."); + } + }catch(Exception e) { + logger.error("Exception !",e); + } + + return null; + } + +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorEmailAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorEmailAuthenticationProvider.java new file mode 100644 index 000000000..193578e35 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorEmailAuthenticationProvider.java @@ -0,0 +1,90 @@ + + + +package org.dromara.maxkey.authn.provider.twofactor.impl; + +import org.dromara.maxkey.authn.LoginCredential; +import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; +import org.dromara.maxkey.entity.idm.UserInfo; +import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn; +import org.dromara.maxkey.password.onetimepwd.MailOtpAuthnService; +import org.dromara.maxkey.web.WebConstants; +import org.dromara.maxkey.web.WebContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + + +/** + * TwoFactor Authentication provider.二次认证邮件认证提供者 + * + * @author Crystal.Sea + * + */ +public class TwoFactorEmailAuthenticationProvider extends AbstractAuthenticationProvider { + private static final Logger logger = LoggerFactory.getLogger(TwoFactorEmailAuthenticationProvider.class); + + MailOtpAuthnService mailOtpAuthnService; + + public String getProviderName() { + return "twoFactorEmail" + PROVIDER_SUFFIX; + } + + public TwoFactorEmailAuthenticationProvider(MailOtpAuthnService mailOtpAuthnService) { + this.mailOtpAuthnService = mailOtpAuthnService; + } + + @Override + public Authentication doAuthenticate(LoginCredential credential) { + return null; + } + + @Override + public Authentication doTwoFactorAuthenticate(LoginCredential credential,UserInfo user) { + UsernamePasswordAuthenticationToken authenticationToken = null; + logger.debug("loginCredential {}" , credential); + try { + //短信验证码校验 + matches(credential.getOtpCaptcha(),user); + + authenticationToken = new UsernamePasswordAuthenticationToken(credential.getUsername(),"email"); + + } catch (AuthenticationException e) { + logger.error("Failed to authenticate user {} via {}: {}",credential.getPrincipal(), + getProviderName(), + e.getMessage() ); + WebContext.setAttribute( + WebConstants.LOGIN_ERROR_SESSION_MESSAGE, e.getMessage()); + } catch (Exception e) { + logger.error("Login error Unexpected exception in {} authentication:\n{}" , + getProviderName(), e.getMessage()); + } + + return authenticationToken; + } + + + /** + * mobile validate.手机验证码校验 + * + * @param otpCaptcha String + * @param authType String + * @param userInfo UserInfo + */ + protected void matches(String captcha, UserInfo userInfo) { + // for mobile password + UserInfo validUserInfo = new UserInfo(); + validUserInfo.setUsername(userInfo.getUsername()); + validUserInfo.setId(userInfo.getId()); + AbstractOtpAuthn smsOtpAuthn = mailOtpAuthnService.getMailOtpAuthn(userInfo.getInstId()); + if (captcha == null || !smsOtpAuthn.validate(validUserInfo, captcha)) { + String message = WebContext.getI18nValue("login.error.captcha"); + logger.debug("login captcha valid error."); + throw new BadCredentialsException(message); + } + } + +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorMobileAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorMobileAuthenticationProvider.java new file mode 100644 index 000000000..da34183a3 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorMobileAuthenticationProvider.java @@ -0,0 +1,88 @@ + + + +package org.dromara.maxkey.authn.provider.twofactor.impl; + +import org.dromara.maxkey.authn.LoginCredential; +import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; +import org.dromara.maxkey.entity.idm.UserInfo; +import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn; +import org.dromara.maxkey.password.sms.SmsOtpAuthnService; +import org.dromara.maxkey.web.WebConstants; +import org.dromara.maxkey.web.WebContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + + +/** + * TwoFactorMobile Authentication provider.二次认证手机认证提供者 + * + * @author Crystal.Sea + * + */ +public class TwoFactorMobileAuthenticationProvider extends AbstractAuthenticationProvider { + private static final Logger logger = LoggerFactory.getLogger(TwoFactorMobileAuthenticationProvider.class); + + SmsOtpAuthnService smsOtpAuthnService; + + public String getProviderName() { + return "twoFactorMobile" + PROVIDER_SUFFIX; + } + + public TwoFactorMobileAuthenticationProvider(SmsOtpAuthnService smsOtpAuthnService) { + this.smsOtpAuthnService = smsOtpAuthnService; + } + + @Override + public Authentication doAuthenticate(LoginCredential credential) { + return null; + } + + @Override + public Authentication doTwoFactorAuthenticate(LoginCredential credential,UserInfo user) { + UsernamePasswordAuthenticationToken authenticationToken = null; + logger.debug("loginCredential {}" , credential); + try { + //短信验证码校验 + matches(credential.getOtpCaptcha(),user); + + authenticationToken = new UsernamePasswordAuthenticationToken(credential.getUsername(),"mobile"); + + } catch (AuthenticationException e) { + logger.error("Failed to authenticate user {} via {}: {}",credential.getPrincipal(), + getProviderName(), + e.getMessage() ); + WebContext.setAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE, e.getMessage()); + } catch (Exception e) { + logger.error("Login error Unexpected exception in {} authentication:\n{}" ,getProviderName(), e.getMessage()); + } + + return authenticationToken; + } + + + /** + * mobile validate.手机验证码校验 + * + * @param otpCaptcha String + * @param authType String + * @param userInfo UserInfo + */ + protected void matches(String captcha, UserInfo userInfo) { + // for mobile password + UserInfo validUserInfo = new UserInfo(); + validUserInfo.setUsername(userInfo.getUsername()); + validUserInfo.setId(userInfo.getId()); + AbstractOtpAuthn smsOtpAuthn = smsOtpAuthnService.getByInstId(userInfo.getInstId()); + if (captcha == null || !smsOtpAuthn.validate(validUserInfo, captcha)) { + String message = WebContext.getI18nValue("login.error.captcha"); + logger.debug("login captcha valid error."); + throw new BadCredentialsException(message); + } + } + +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorTotpAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorTotpAuthenticationProvider.java new file mode 100644 index 000000000..2c0007af2 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/twofactor/impl/TwoFactorTotpAuthenticationProvider.java @@ -0,0 +1,84 @@ + + +package org.dromara.maxkey.authn.provider.twofactor.impl; + +import org.dromara.maxkey.authn.LoginCredential; +import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; +import org.dromara.maxkey.authn.realm.AbstractAuthenticationRealm; +import org.dromara.maxkey.entity.idm.UserInfo; +import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn; +import org.dromara.maxkey.web.WebConstants; +import org.dromara.maxkey.web.WebContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + + +/** + * TwoFactorTotp Authentication provider.二次认证TOTP认证提供者 + * + * @author Crystal.Sea + * + */ +public class TwoFactorTotpAuthenticationProvider extends AbstractAuthenticationProvider { + private static final Logger logger = LoggerFactory.getLogger(TwoFactorTotpAuthenticationProvider.class); + + public String getProviderName() { + return "twoFactorTotp" + PROVIDER_SUFFIX; + } + + public TwoFactorTotpAuthenticationProvider(AbstractAuthenticationRealm authenticationRealm,AbstractOtpAuthn tfaOtpAuthn) { + this.authenticationRealm = authenticationRealm; + this.tfaOtpAuthn = tfaOtpAuthn; + } + + @Override + public Authentication doAuthenticate(LoginCredential credential) { + return null; + } + + @Override + public Authentication doTwoFactorAuthenticate(LoginCredential credential,UserInfo user) { + UsernamePasswordAuthenticationToken authenticationToken = null; + logger.debug("loginCredential {}" , credential); + try { + //验证码校验 + UserInfo userTotp = authenticationRealm.loadUserInfoById(user.getId()); + + matches(credential.getOtpCaptcha(),userTotp.getSharedSecret()); + + authenticationToken = new UsernamePasswordAuthenticationToken(credential.getUsername(),"TOTP"); + + } catch (AuthenticationException e) { + logger.error("Failed to authenticate user {} via {}: {}",credential.getPrincipal(), + getProviderName(), + e.getMessage() ); + WebContext.setAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE, e.getMessage()); + } catch (Exception e) { + logger.error("Login error Unexpected exception in {} authentication:\n{}" , getProviderName(), e.getMessage()); + } + + return authenticationToken; + } + + + /** + * 双因素验证. + * + * @param otpCaptcha String + * @param authType String + * @param userInfo UserInfo + */ + protected void matches(String captcha, String sharedSecret) { + // for one time password 2 factor + if (captcha == null || !tfaOtpAuthn.validate(sharedSecret, captcha)) { + String message = WebContext.getI18nValue("login.error.captcha"); + logger.debug("login captcha valid error."); + throw new BadCredentialsException(message); + } + } + +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/realm/AbstractAuthenticationRealm.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/realm/AbstractAuthenticationRealm.java index 27914f57f..b13c6699c 100644 --- a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/realm/AbstractAuthenticationRealm.java +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/realm/AbstractAuthenticationRealm.java @@ -85,6 +85,10 @@ public abstract class AbstractAuthenticationRealm { public UserInfo loadUserInfo(String username, String password) { return loginService.find(username, password); } + + public UserInfo loadUserInfoById(String userId) { + return loginService.findById(userId); + } public abstract boolean passwordMatches(UserInfo userInfo, String password); diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java index 772061310..7c688640e 100644 --- a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java @@ -21,17 +21,27 @@ import org.dromara.maxkey.authn.jwt.AuthTokenService; import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; import org.dromara.maxkey.authn.provider.AuthenticationProviderFactory; import org.dromara.maxkey.authn.provider.impl.*; +import org.dromara.maxkey.authn.provider.twofactor.TwoFactorAuthenticationProvider; +import org.dromara.maxkey.authn.provider.twofactor.impl.TwoFactorEmailAuthenticationProvider; +import org.dromara.maxkey.authn.provider.twofactor.impl.TwoFactorMobileAuthenticationProvider; +import org.dromara.maxkey.authn.provider.twofactor.impl.TwoFactorTotpAuthenticationProvider; import org.dromara.maxkey.authn.realm.AbstractAuthenticationRealm; import org.dromara.maxkey.authn.session.SessionManager; import org.dromara.maxkey.authn.support.rememberme.AbstractRemeberMeManager; import org.dromara.maxkey.authn.support.rememberme.JdbcRemeberMeManager; import org.dromara.maxkey.configuration.ApplicationConfig; +import org.dromara.maxkey.constants.ConstsTwoFactor; +import org.dromara.maxkey.ip2location.IpLocationParser; +import org.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn; +import org.dromara.maxkey.password.onetimepwd.MailOtpAuthnService; import org.dromara.maxkey.password.sms.SmsOtpAuthnService; import org.dromara.maxkey.persistence.service.CnfPasswordPolicyService; +import org.dromara.maxkey.persistence.service.LoginService; import org.dromara.maxkey.persistence.service.PasswordPolicyValidatorService; import org.dromara.maxkey.persistence.service.impl.PasswordPolicyValidatorServiceImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.MessageSource; @@ -49,7 +59,8 @@ public class AuthnProviderAutoConfiguration { MobileAuthenticationProvider mobileAuthenticationProvider, TrustedAuthenticationProvider trustedAuthenticationProvider, ScanCodeAuthenticationProvider scanCodeAuthenticationProvider, - AppAuthenticationProvider appAuthenticationProvider + AppAuthenticationProvider appAuthenticationProvider, + TwoFactorAuthenticationProvider twoFactorAuthenticationProvider ) { AuthenticationProviderFactory authenticationProvider = new AuthenticationProviderFactory(); authenticationProvider.addAuthenticationProvider(normalAuthenticationProvider); @@ -58,6 +69,9 @@ public class AuthnProviderAutoConfiguration { authenticationProvider.addAuthenticationProvider(scanCodeAuthenticationProvider); authenticationProvider.addAuthenticationProvider(appAuthenticationProvider); + //二次认证 + authenticationProvider.addAuthenticationProvider(twoFactorAuthenticationProvider); + return authenticationProvider; } @@ -155,5 +169,48 @@ public class AuthnProviderAutoConfiguration { return new JdbcRemeberMeManager( jdbcTemplate,applicationConfig,authTokenService,validity); } + + @Bean + TwoFactorAuthenticationProvider twoFactorAuthenticationProvider( + AbstractAuthenticationRealm authenticationRealm, + SessionManager sessionManager, + LoginService loginService, + AuthTokenService authTokenService, + IpLocationParser ipLocationParser, + TwoFactorTotpAuthenticationProvider twoFactorTotpAuthenticationProvider, + TwoFactorMobileAuthenticationProvider twoFactorMobileAuthenticationProvider, + TwoFactorEmailAuthenticationProvider twoFactorEmailAuthenticationProvider) { + _logger.debug("init TwoFactor authentication Provider ."); + TwoFactorAuthenticationProvider twoFactorProvider =new TwoFactorAuthenticationProvider( + authenticationRealm, + sessionManager, + loginService, + authTokenService + ); + + twoFactorProvider.addProvider(ConstsTwoFactor.TOTP, twoFactorTotpAuthenticationProvider); + twoFactorProvider.addProvider(ConstsTwoFactor.EMAIL, twoFactorEmailAuthenticationProvider); + twoFactorProvider.addProvider(ConstsTwoFactor.SMS, twoFactorMobileAuthenticationProvider); + return twoFactorProvider; + } + + @Bean + TwoFactorTotpAuthenticationProvider twoFactorTotpAuthenticationProvider(@Qualifier("tfaOtpAuthn") AbstractOtpAuthn tfaOtpAuthn, + AbstractAuthenticationRealm authenticationRealm) { + _logger.debug("init TwoFactor authentication Provider ."); + return new TwoFactorTotpAuthenticationProvider(authenticationRealm,tfaOtpAuthn); + } + + @Bean + TwoFactorMobileAuthenticationProvider twoFactorMobileAuthenticationProvider(SmsOtpAuthnService smsOtpAuthnService) { + _logger.debug("init TwoFactor Mobile authentication Provider ."); + return new TwoFactorMobileAuthenticationProvider(smsOtpAuthnService); + } + + @Bean + TwoFactorEmailAuthenticationProvider twoFactorEmailAuthenticationProvider(MailOtpAuthnService mailOtpAuthnService) { + _logger.debug("init TwoFactor Email authentication Provider ."); + return new TwoFactorEmailAuthenticationProvider(mailOtpAuthnService); + } } diff --git a/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsJwt.java b/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsJwt.java new file mode 100644 index 000000000..0956e2ca7 --- /dev/null +++ b/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsJwt.java @@ -0,0 +1,30 @@ + + +package org.dromara.maxkey.constants; + +/** + * Jwt. + * @author Crystal.Sea + * + */ +public final class ConstsJwt { + + public static final String ACCESS_TOKEN = "access_token"; + + public static final String REFRESH_TOKEN = "refresh_token"; + + public static final String EXPIRES_IN = "expired"; + + public static final String INST_ID = "instId"; + + public static final String USER_ID = "userId"; + + public static final String STYLE = "style"; + + public static final String TWO_FACTOR = "twoFactor"; + + public static final String KID = "kid"; + + public static final String LOCALE = "locale"; + +} diff --git a/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsLoginType.java b/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsLoginType.java index 1a2f2eafe..0145a1466 100644 --- a/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsLoginType.java +++ b/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsLoginType.java @@ -43,4 +43,19 @@ public class ConstsLoginType { public static final String HTTPHEADER = "HttpHeader"; + + public static final class TwoFactor{ + /** + * 1=TOTP(动态验证码) + */ + public static final String TWO_FACTOR_TOTP = "TwoFactorTotp"; + /** + * 2=邮箱验证码 + */ + public static final String TWO_FACTOR_EMAIL = "TwoFactorEmail"; + /** + * 3=手机短信 + */ + public static final String TWO_FACTOR_MOBILE = "TwoFactorMobile"; + } } diff --git a/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsTwoFactor.java b/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsTwoFactor.java new file mode 100644 index 000000000..ef0acc4e6 --- /dev/null +++ b/maxkey-commons/maxkey-common/src/main/java/org/dromara/maxkey/constants/ConstsTwoFactor.java @@ -0,0 +1,27 @@ + + +package org.dromara.maxkey.constants; + +/** + * 二次认证验证码 + */ +public class ConstsTwoFactor { + + /** + * 无 + */ + public static final int NONE = 0; + + /** + * 动态令牌TOTP + */ + public static final int TOTP = 1; + /** + * 邮件验证码 + */ + public static final int EMAIL = 2; + /** + * 短信验证码 + */ + public static final int SMS = 3; +} diff --git a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/LoginService.java b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/LoginService.java index 6d81cfc8d..dc14e622b 100644 --- a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/LoginService.java +++ b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/LoginService.java @@ -27,6 +27,8 @@ public interface LoginService { public UserInfo find(String username, String password); + public UserInfo findById(String userId); + public List findByUsername(String username, String password); public List findByUsernameOrMobile(String username, String password); diff --git a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/impl/LoginServiceImpl.java b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/impl/LoginServiceImpl.java index c6004b80f..8e5f4ee7e 100644 --- a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/impl/LoginServiceImpl.java +++ b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/service/impl/LoginServiceImpl.java @@ -58,6 +58,8 @@ public class LoginServiceImpl implements LoginService{ private static final String GROUPS_SELECT_STATEMENT = "select distinct g.id,g.groupcode,g.groupname from mxk_userinfo u,mxk_groups g,mxk_group_member gm where u.id = ? and u.id=gm.memberid and gm.groupid=g.id "; private static final String DEFAULT_USERINFO_SELECT_STATEMENT = "select * from mxk_userinfo where username = ? "; + + private static final String DEFAULT_USERINFO_SELECT_STATEMENT_BY_ID = "select * from mxk_userinfo where id = ? "; private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE = "select * from mxk_userinfo where (username = ? or mobile = ?)"; @@ -491,6 +493,16 @@ public class LoginServiceImpl implements LoginService{ return userInfo; } } + + @Override + public UserInfo findById(String userId) { + List listUserInfo = jdbcTemplate.query( + DEFAULT_USERINFO_SELECT_STATEMENT_BY_ID, + new UserInfoRowMapper(), + userId + ); + return (CollectionUtils.isNotEmpty(listUserInfo) ? listUserInfo.get(0) : null); + } } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/AbstractOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/AbstractOtpAuthn.java index 7a4551761..94436f2ae 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/AbstractOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/AbstractOtpAuthn.java @@ -69,6 +69,8 @@ public abstract class AbstractOtpAuthn { public abstract boolean produce(UserInfo userInfo); public abstract boolean validate(UserInfo userInfo, String token); + + public abstract boolean validate(String sharedSecret, String token); protected String defaultProduce(UserInfo userInfo) { return genToken(userInfo); diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CapOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CapOtpAuthn.java index 091e4e80b..acb1459de 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CapOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CapOtpAuthn.java @@ -48,4 +48,10 @@ public class CapOtpAuthn extends AbstractOtpAuthn { return false; } + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CounterBasedOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CounterBasedOtpAuthn.java index 96ad1e9cc..8b82bc73f 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CounterBasedOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/CounterBasedOtpAuthn.java @@ -72,4 +72,10 @@ public class CounterBasedOtpAuthn extends AbstractOtpAuthn { return false; } + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/HotpOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/HotpOtpAuthn.java index 1d4f75d93..f9c56699f 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/HotpOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/HotpOtpAuthn.java @@ -95,4 +95,10 @@ public class HotpOtpAuthn extends AbstractOtpAuthn { this.truncation = truncation; } + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MailOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MailOtpAuthn.java index 8343563a2..26a6e57af 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MailOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MailOtpAuthn.java @@ -121,6 +121,12 @@ public class MailOtpAuthn extends AbstractOtpAuthn { public void setMessageTemplate(String messageTemplate) { this.messageTemplate = messageTemplate; } + + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MobileOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MobileOtpAuthn.java index 5b300a365..5070c1fd9 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MobileOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/MobileOtpAuthn.java @@ -38,4 +38,10 @@ public class MobileOtpAuthn extends AbstractOtpAuthn { return false; } + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/RsaOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/RsaOtpAuthn.java index ff5dc3571..78b896f48 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/RsaOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/RsaOtpAuthn.java @@ -48,4 +48,10 @@ public class RsaOtpAuthn extends AbstractOtpAuthn { return false; } + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/TimeBasedOtpAuthn.java b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/TimeBasedOtpAuthn.java index a3c3fad1d..84242ba7a 100644 --- a/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/TimeBasedOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-otp/src/main/java/org/dromara/maxkey/password/onetimepwd/impl/TimeBasedOtpAuthn.java @@ -51,9 +51,14 @@ public class TimeBasedOtpAuthn extends AbstractOtpAuthn { @Override public boolean validate(UserInfo userInfo, String token) { - _logger.debug("utcTime : {}" , dateFormat.format(new Date())); + return validate(userInfo.getSharedSecret() , token); + } + + @Override + public boolean validate(String secret, String token) { + _logger.debug("utcTime : {}" , dateFormat.format(new Date())); long currentTimeSeconds = System.currentTimeMillis() / 1000; - String sharedSecret = PasswordReciprocal.getInstance().decoder(userInfo.getSharedSecret()); + String sharedSecret = PasswordReciprocal.getInstance().decoder(secret); byte[] byteSharedSecret = Base32Utils.decode(sharedSecret); String hexSharedSecret = Hex.encodeHexString(byteSharedSecret); String timeBasedToken = ""; @@ -79,7 +84,6 @@ public class TimeBasedOtpAuthn extends AbstractOtpAuthn { return true; } return false; - - } + } } diff --git a/maxkey-starter/maxkey-starter-sms/src/main/java/org/dromara/maxkey/password/sms/SmsOtpAuthn.java b/maxkey-starter/maxkey-starter-sms/src/main/java/org/dromara/maxkey/password/sms/SmsOtpAuthn.java index 2c451df19..7e8789a23 100644 --- a/maxkey-starter/maxkey-starter-sms/src/main/java/org/dromara/maxkey/password/sms/SmsOtpAuthn.java +++ b/maxkey-starter/maxkey-starter-sms/src/main/java/org/dromara/maxkey/password/sms/SmsOtpAuthn.java @@ -56,4 +56,10 @@ public class SmsOtpAuthn extends AbstractOtpAuthn { } + @Override + public boolean validate(String sharedSecret, String token) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/maxkey-web-frontend/maxkey-web-app/package-lock.json b/maxkey-web-frontend/maxkey-web-app/package-lock.json index 34e76517e..7f3c96288 100644 --- a/maxkey-web-frontend/maxkey-web-app/package-lock.json +++ b/maxkey-web-frontend/maxkey-web-app/package-lock.json @@ -1,12 +1,12 @@ { "name": "maxkey", - "version": "3.5.0", + "version": "4.1.x", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "maxkey", - "version": "3.5.0", + "version": "4.1.x", "license": "Apache-2.0", "dependencies": { "@angular/animations": "~13.3.0", @@ -14872,11 +14872,15 @@ }, "node_modules/npm/node_modules/@gar/promisify": { "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "inBundle": true, "license": "ISC" }, @@ -14929,6 +14933,8 @@ }, "node_modules/npm/node_modules/@npmcli/ci-detect": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@npmcli/ci-detect/-/ci-detect-2.0.0.tgz", + "integrity": "sha512-8yQtQ9ArHh/TzdUDKQwEvwCgpDuhSWTDAbiKMl3854PcT+Dk4UmWaiawuFTLy9n5twzXOBXVflWe+90/ffXQrA==", "inBundle": true, "license": "ISC", "engines": { @@ -14997,6 +15003,8 @@ }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -15026,6 +15034,8 @@ }, "node_modules/npm/node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15071,6 +15081,8 @@ }, "node_modules/npm/node_modules/@npmcli/name-from-folder": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", + "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", "inBundle": true, "license": "ISC" }, @@ -15089,6 +15101,8 @@ }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", "inBundle": true, "license": "ISC", "dependencies": { @@ -15111,6 +15125,8 @@ }, "node_modules/npm/node_modules/@tootallnate/once": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "inBundle": true, "license": "MIT", "engines": { @@ -15119,11 +15135,15 @@ }, "node_modules/npm/node_modules/abbrev": { "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15148,6 +15168,8 @@ }, "node_modules/npm/node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15160,6 +15182,8 @@ }, "node_modules/npm/node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "inBundle": true, "license": "MIT", "engines": { @@ -15168,6 +15192,8 @@ }, "node_modules/npm/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15192,11 +15218,15 @@ }, "node_modules/npm/node_modules/aproba": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/archy": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "inBundle": true, "license": "MIT" }, @@ -15214,11 +15244,15 @@ }, "node_modules/npm/node_modules/asap": { "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "inBundle": true, "license": "MIT" }, @@ -15248,6 +15282,8 @@ }, "node_modules/npm/node_modules/brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15257,6 +15293,8 @@ }, "node_modules/npm/node_modules/builtins": { "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "inBundle": true, "license": "MIT" }, @@ -15290,6 +15328,8 @@ }, "node_modules/npm/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15305,6 +15345,8 @@ }, "node_modules/npm/node_modules/chownr": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "inBundle": true, "license": "ISC", "engines": { @@ -15313,6 +15355,8 @@ }, "node_modules/npm/node_modules/cidr-regex": { "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/cidr-regex/-/cidr-regex-3.1.1.tgz", + "integrity": "sha512-RBqYd32aDwbCMFJRL6wHOlDNYJsPNTt8vC82ErHF5vKt8QQzxm1FrkW8s/R5pVrXMf17sba09Uoy91PKiddAsw==", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -15324,6 +15368,8 @@ }, "node_modules/npm/node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "inBundle": true, "license": "MIT", "engines": { @@ -15332,6 +15378,8 @@ }, "node_modules/npm/node_modules/cli-columns": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cli-columns/-/cli-columns-4.0.0.tgz", + "integrity": "sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15358,6 +15406,8 @@ }, "node_modules/npm/node_modules/clone": { "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "inBundle": true, "license": "MIT", "engines": { @@ -15377,6 +15427,8 @@ }, "node_modules/npm/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15388,11 +15440,15 @@ }, "node_modules/npm/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/color-support": { "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "inBundle": true, "license": "ISC", "bin": { @@ -15401,6 +15457,8 @@ }, "node_modules/npm/node_modules/colors": { "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "inBundle": true, "license": "MIT", "optional": true, @@ -15410,6 +15468,8 @@ }, "node_modules/npm/node_modules/columnify": { "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15422,16 +15482,22 @@ }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/console-control-strings": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "inBundle": true, "license": "ISC" }, @@ -15453,11 +15519,15 @@ }, "node_modules/npm/node_modules/debug/node_modules/ms": { "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/debuglog": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", "inBundle": true, "license": "MIT", "engines": { @@ -15474,6 +15544,8 @@ }, "node_modules/npm/node_modules/delegates": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "inBundle": true, "license": "MIT" }, @@ -15504,11 +15576,15 @@ }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/encoding": { "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "inBundle": true, "license": "MIT", "optional": true, @@ -15518,6 +15594,8 @@ }, "node_modules/npm/node_modules/env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "inBundle": true, "license": "MIT", "engines": { @@ -15526,6 +15604,8 @@ }, "node_modules/npm/node_modules/err-code": { "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "inBundle": true, "license": "MIT" }, @@ -15536,6 +15616,8 @@ }, "node_modules/npm/node_modules/fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "inBundle": true, "license": "ISC", "dependencies": { @@ -15547,6 +15629,8 @@ }, "node_modules/npm/node_modules/fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "inBundle": true, "license": "ISC" }, @@ -15610,6 +15694,8 @@ }, "node_modules/npm/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "inBundle": true, "license": "MIT", "engines": { @@ -15618,6 +15704,8 @@ }, "node_modules/npm/node_modules/has-unicode": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "inBundle": true, "license": "ISC" }, @@ -15639,6 +15727,8 @@ }, "node_modules/npm/node_modules/http-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15664,6 +15754,8 @@ }, "node_modules/npm/node_modules/humanize-ms": { "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -15672,6 +15764,8 @@ }, "node_modules/npm/node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "inBundle": true, "license": "MIT", "optional": true, @@ -15684,6 +15778,8 @@ }, "node_modules/npm/node_modules/ignore-walk": { "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -15695,6 +15791,8 @@ }, "node_modules/npm/node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "inBundle": true, "license": "MIT", "engines": { @@ -15703,6 +15801,8 @@ }, "node_modules/npm/node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "inBundle": true, "license": "MIT", "engines": { @@ -15711,11 +15811,15 @@ }, "node_modules/npm/node_modules/infer-owner": { "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "inBundle": true, "license": "ISC", "dependencies": { @@ -15725,6 +15829,8 @@ }, "node_modules/npm/node_modules/inherits": { "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "inBundle": true, "license": "ISC" }, @@ -15760,6 +15866,8 @@ }, "node_modules/npm/node_modules/ip-regex": { "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", "inBundle": true, "license": "MIT", "engines": { @@ -15768,6 +15876,8 @@ }, "node_modules/npm/node_modules/is-cidr": { "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/is-cidr/-/is-cidr-4.0.2.tgz", + "integrity": "sha512-z4a1ENUajDbEl/Q6/pVBpTR1nBjjEE1X7qb7bmWYanNnPoKAvUCPFKeXV6Fe4mgTkWKBqiHIcwsI3SndiO5FeA==", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -15790,6 +15900,8 @@ }, "node_modules/npm/node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "inBundle": true, "license": "MIT", "engines": { @@ -15798,21 +15910,29 @@ }, "node_modules/npm/node_modules/is-lambda": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/isexe": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", "inBundle": true, "license": "ISC", "funding": { @@ -15821,6 +15941,8 @@ }, "node_modules/npm/node_modules/jsonparse": { "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "engines": [ "node >= 0.2.0" ], @@ -16029,6 +16151,8 @@ }, "node_modules/npm/node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16051,6 +16175,8 @@ }, "node_modules/npm/node_modules/minipass-collect": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16078,6 +16204,8 @@ }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16098,6 +16226,8 @@ }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16109,6 +16239,8 @@ }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16120,6 +16252,8 @@ }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16132,6 +16266,8 @@ }, "node_modules/npm/node_modules/mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "inBundle": true, "license": "MIT", "bin": { @@ -16143,6 +16279,8 @@ }, "node_modules/npm/node_modules/mkdirp-infer-owner": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz", + "integrity": "sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16156,11 +16294,15 @@ }, "node_modules/npm/node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "inBundle": true, "license": "ISC" }, @@ -16197,6 +16339,8 @@ }, "node_modules/npm/node_modules/nopt": { "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16236,6 +16380,8 @@ }, "node_modules/npm/node_modules/npm-bundled": { "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16244,6 +16390,8 @@ }, "node_modules/npm/node_modules/npm-install-checks": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -16255,6 +16403,8 @@ }, "node_modules/npm/node_modules/npm-normalize-package-bin": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "inBundle": true, "license": "ISC" }, @@ -16333,6 +16483,8 @@ }, "node_modules/npm/node_modules/npm-user-validate": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz", + "integrity": "sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==", "inBundle": true, "license": "BSD-2-Clause" }, @@ -16352,6 +16504,8 @@ }, "node_modules/npm/node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16360,6 +16514,8 @@ }, "node_modules/npm/node_modules/opener": { "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "inBundle": true, "license": "(WTFPL OR MIT)", "bin": { @@ -16368,6 +16524,8 @@ }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16429,6 +16587,8 @@ }, "node_modules/npm/node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "inBundle": true, "license": "MIT", "engines": { @@ -16445,6 +16605,8 @@ }, "node_modules/npm/node_modules/promise-all-reject-late": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", "inBundle": true, "license": "ISC", "funding": { @@ -16461,11 +16623,15 @@ }, "node_modules/npm/node_modules/promise-inflight": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/promise-retry": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16478,6 +16644,8 @@ }, "node_modules/npm/node_modules/promzard": { "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/promzard/-/promzard-0.3.0.tgz", + "integrity": "sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16486,6 +16654,8 @@ }, "node_modules/npm/node_modules/qrcode-terminal": { "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "inBundle": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" @@ -16493,6 +16663,8 @@ }, "node_modules/npm/node_modules/read": { "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16523,6 +16695,8 @@ }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16548,6 +16722,8 @@ }, "node_modules/npm/node_modules/readdir-scoped-modules": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16559,6 +16735,8 @@ }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "inBundle": true, "license": "MIT", "engines": { @@ -16567,6 +16745,8 @@ }, "node_modules/npm/node_modules/rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16581,6 +16761,8 @@ }, "node_modules/npm/node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -16600,6 +16782,8 @@ }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "inBundle": true, "license": "MIT", "optional": true @@ -16620,6 +16804,8 @@ }, "node_modules/npm/node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16631,16 +16817,22 @@ }, "node_modules/npm/node_modules/set-blocking": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "inBundle": true, "license": "MIT", "engines": { @@ -16690,6 +16882,8 @@ }, "node_modules/npm/node_modules/spdx-expression-parse": { "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16704,6 +16898,8 @@ }, "node_modules/npm/node_modules/ssri": { "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16715,6 +16911,8 @@ }, "node_modules/npm/node_modules/string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16723,6 +16921,8 @@ }, "node_modules/npm/node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16741,6 +16941,8 @@ }, "node_modules/npm/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16752,6 +16954,8 @@ }, "node_modules/npm/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16779,11 +16983,15 @@ }, "node_modules/npm/node_modules/text-table": { "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz", + "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==", "inBundle": true, "license": "MIT" }, @@ -16794,6 +17002,8 @@ }, "node_modules/npm/node_modules/unique-filename": { "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16802,6 +17012,8 @@ }, "node_modules/npm/node_modules/unique-slug": { "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16810,11 +17022,15 @@ }, "node_modules/npm/node_modules/util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/validate-npm-package-license": { "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -16824,6 +17040,8 @@ }, "node_modules/npm/node_modules/validate-npm-package-name": { "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16832,11 +17050,15 @@ }, "node_modules/npm/node_modules/walk-up-path": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/walk-up-path/-/walk-up-path-1.0.0.tgz", + "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/wcwidth": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -16845,6 +17067,8 @@ }, "node_modules/npm/node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16859,6 +17083,8 @@ }, "node_modules/npm/node_modules/wide-align": { "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "inBundle": true, "license": "ISC", "dependencies": { @@ -16867,6 +17093,8 @@ }, "node_modules/npm/node_modules/wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "inBundle": true, "license": "ISC" }, @@ -16884,6 +17112,8 @@ }, "node_modules/npm/node_modules/yallist": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "inBundle": true, "license": "ISC" }, @@ -34921,10 +35151,14 @@ "dependencies": { "@gar/promisify": { "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "bundled": true }, "@isaacs/string-locale-compare": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "bundled": true }, "@npmcli/arborist": { @@ -34969,6 +35203,8 @@ }, "@npmcli/ci-detect": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@npmcli/ci-detect/-/ci-detect-2.0.0.tgz", + "integrity": "sha512-8yQtQ9ArHh/TzdUDKQwEvwCgpDuhSWTDAbiKMl3854PcT+Dk4UmWaiawuFTLy9n5twzXOBXVflWe+90/ffXQrA==", "bundled": true }, "@npmcli/config": { @@ -35017,6 +35253,8 @@ }, "@npmcli/installed-package-contents": { "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", "bundled": true, "requires": { "npm-bundled": "^1.1.1", @@ -35035,6 +35273,8 @@ "dependencies": { "brace-expansion": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "bundled": true, "requires": { "balanced-match": "^1.0.0" @@ -35069,6 +35309,8 @@ }, "@npmcli/name-from-folder": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", + "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", "bundled": true }, "@npmcli/node-gyp": { @@ -35084,6 +35326,8 @@ }, "@npmcli/promise-spawn": { "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", "bundled": true, "requires": { "infer-owner": "^1.0.4" @@ -35101,14 +35345,20 @@ }, "@tootallnate/once": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "bundled": true }, "abbrev": { "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "bundled": true }, "agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "bundled": true, "requires": { "debug": "4" @@ -35125,6 +35375,8 @@ }, "aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "bundled": true, "requires": { "clean-stack": "^2.0.0", @@ -35133,10 +35385,14 @@ }, "ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "bundled": true }, "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "bundled": true, "requires": { "color-convert": "^2.0.1" @@ -35152,10 +35408,14 @@ }, "aproba": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "bundled": true }, "archy": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "bundled": true }, "are-we-there-yet": { @@ -35168,10 +35428,14 @@ }, "asap": { "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "bundled": true }, "balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "bundled": true }, "bin-links": { @@ -35192,6 +35456,8 @@ }, "brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "bundled": true, "requires": { "balanced-match": "^1.0.0", @@ -35200,6 +35466,8 @@ }, "builtins": { "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "bundled": true }, "cacache": { @@ -35228,6 +35496,8 @@ }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "bundled": true, "requires": { "ansi-styles": "^4.1.0", @@ -35236,10 +35506,14 @@ }, "chownr": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "bundled": true }, "cidr-regex": { "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/cidr-regex/-/cidr-regex-3.1.1.tgz", + "integrity": "sha512-RBqYd32aDwbCMFJRL6wHOlDNYJsPNTt8vC82ErHF5vKt8QQzxm1FrkW8s/R5pVrXMf17sba09Uoy91PKiddAsw==", "bundled": true, "requires": { "ip-regex": "^4.1.0" @@ -35247,10 +35521,14 @@ }, "clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "bundled": true }, "cli-columns": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cli-columns/-/cli-columns-4.0.0.tgz", + "integrity": "sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ==", "bundled": true, "requires": { "string-width": "^4.2.3", @@ -35267,6 +35545,8 @@ }, "clone": { "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "bundled": true }, "cmd-shim": { @@ -35278,6 +35558,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "bundled": true, "requires": { "color-name": "~1.1.4" @@ -35285,19 +35567,27 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "bundled": true }, "color-support": { "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "bundled": true }, "colors": { "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "bundled": true, "optional": true }, "columnify": { "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "bundled": true, "requires": { "strip-ansi": "^6.0.1", @@ -35306,14 +35596,20 @@ }, "common-ancestor-path": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", "bundled": true }, "concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "bundled": true }, "console-control-strings": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "bundled": true }, "debug": { @@ -35325,12 +35621,16 @@ "dependencies": { "ms": { "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "bundled": true } } }, "debuglog": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", "bundled": true }, "defaults": { @@ -35342,6 +35642,8 @@ }, "delegates": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "bundled": true }, "depd": { @@ -35362,10 +35664,14 @@ }, "emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "bundled": true }, "encoding": { "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "bundled": true, "optional": true, "requires": { @@ -35374,10 +35680,14 @@ }, "env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "bundled": true }, "err-code": { "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "bundled": true }, "fastest-levenshtein": { @@ -35386,6 +35696,8 @@ }, "fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "bundled": true, "requires": { "minipass": "^3.0.0" @@ -35393,6 +35705,8 @@ }, "fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "bundled": true }, "function-bind": { @@ -35438,10 +35752,14 @@ }, "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "bundled": true }, "has-unicode": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "bundled": true }, "hosted-git-info": { @@ -35457,6 +35775,8 @@ }, "http-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "bundled": true, "requires": { "@tootallnate/once": "2", @@ -35474,6 +35794,8 @@ }, "humanize-ms": { "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "bundled": true, "requires": { "ms": "^2.0.0" @@ -35481,6 +35803,8 @@ }, "iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "bundled": true, "optional": true, "requires": { @@ -35489,6 +35813,8 @@ }, "ignore-walk": { "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", "bundled": true, "requires": { "minimatch": "^3.0.4" @@ -35496,18 +35822,26 @@ }, "imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "bundled": true }, "indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "bundled": true }, "infer-owner": { "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "bundled": true }, "inflight": { "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "bundled": true, "requires": { "once": "^1.3.0", @@ -35516,6 +35850,8 @@ }, "inherits": { "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "bundled": true }, "ini": { @@ -35541,10 +35877,14 @@ }, "ip-regex": { "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", "bundled": true }, "is-cidr": { "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/is-cidr/-/is-cidr-4.0.2.tgz", + "integrity": "sha512-z4a1ENUajDbEl/Q6/pVBpTR1nBjjEE1X7qb7bmWYanNnPoKAvUCPFKeXV6Fe4mgTkWKBqiHIcwsI3SndiO5FeA==", "bundled": true, "requires": { "cidr-regex": "^3.1.1" @@ -35559,26 +35899,38 @@ }, "is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "bundled": true }, "is-lambda": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "bundled": true }, "isexe": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "bundled": true }, "json-parse-even-better-errors": { "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "bundled": true }, "json-stringify-nice": { "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", "bundled": true }, "jsonparse": { "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "bundled": true }, "just-diff": { @@ -35729,6 +36081,8 @@ }, "minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "bundled": true, "requires": { "brace-expansion": "^1.1.7" @@ -35743,6 +36097,8 @@ }, "minipass-collect": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "bundled": true, "requires": { "minipass": "^3.0.0" @@ -35760,6 +36116,8 @@ }, "minipass-flush": { "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "bundled": true, "requires": { "minipass": "^3.0.0" @@ -35775,6 +36133,8 @@ }, "minipass-pipeline": { "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "bundled": true, "requires": { "minipass": "^3.0.0" @@ -35782,6 +36142,8 @@ }, "minipass-sized": { "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "bundled": true, "requires": { "minipass": "^3.0.0" @@ -35789,6 +36151,8 @@ }, "minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "bundled": true, "requires": { "minipass": "^3.0.0", @@ -35797,10 +36161,14 @@ }, "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "bundled": true }, "mkdirp-infer-owner": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz", + "integrity": "sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==", "bundled": true, "requires": { "chownr": "^2.0.0", @@ -35810,10 +36178,14 @@ }, "ms": { "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "bundled": true }, "mute-stream": { "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "bundled": true }, "negotiator": { @@ -35838,6 +36210,8 @@ }, "nopt": { "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "bundled": true, "requires": { "abbrev": "1" @@ -35862,6 +36236,8 @@ }, "npm-bundled": { "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", "bundled": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -35869,6 +36245,8 @@ }, "npm-install-checks": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", "bundled": true, "requires": { "semver": "^7.1.1" @@ -35876,6 +36254,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "bundled": true }, "npm-package-arg": { @@ -35930,6 +36310,8 @@ }, "npm-user-validate": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz", + "integrity": "sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==", "bundled": true }, "npmlog": { @@ -35944,6 +36326,8 @@ }, "once": { "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "bundled": true, "requires": { "wrappy": "1" @@ -35951,10 +36335,14 @@ }, "opener": { "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "bundled": true }, "p-map": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "bundled": true, "requires": { "aggregate-error": "^3.0.0" @@ -35998,6 +36386,8 @@ }, "path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "bundled": true }, "proc-log": { @@ -36006,6 +36396,8 @@ }, "promise-all-reject-late": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", "bundled": true }, "promise-call-limit": { @@ -36014,10 +36406,14 @@ }, "promise-inflight": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "bundled": true }, "promise-retry": { "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "bundled": true, "requires": { "err-code": "^2.0.2", @@ -36026,6 +36422,8 @@ }, "promzard": { "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/promzard/-/promzard-0.3.0.tgz", + "integrity": "sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw==", "bundled": true, "requires": { "read": "1" @@ -36033,10 +36431,14 @@ }, "qrcode-terminal": { "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "bundled": true }, "read": { "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "bundled": true, "requires": { "mute-stream": "~0.0.4" @@ -36058,6 +36460,8 @@ }, "read-package-json-fast": { "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", "bundled": true, "requires": { "json-parse-even-better-errors": "^2.3.0", @@ -36075,6 +36479,8 @@ }, "readdir-scoped-modules": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "bundled": true, "requires": { "debuglog": "^1.0.1", @@ -36085,10 +36491,14 @@ }, "retry": { "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "bundled": true }, "rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "bundled": true, "requires": { "glob": "^7.1.3" @@ -36096,10 +36506,14 @@ }, "safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "bundled": true }, "safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "bundled": true, "optional": true }, @@ -36112,6 +36526,8 @@ "dependencies": { "lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "bundled": true, "requires": { "yallist": "^4.0.0" @@ -36121,14 +36537,20 @@ }, "set-blocking": { "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "bundled": true }, "signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "bundled": true }, "smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "bundled": true }, "socks": { @@ -36162,6 +36584,8 @@ }, "spdx-expression-parse": { "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "bundled": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -36174,6 +36598,8 @@ }, "ssri": { "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "bundled": true, "requires": { "minipass": "^3.1.1" @@ -36181,6 +36607,8 @@ }, "string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "bundled": true, "requires": { "safe-buffer": "~5.2.0" @@ -36188,6 +36616,8 @@ }, "string-width": { "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "bundled": true, "requires": { "emoji-regex": "^8.0.0", @@ -36201,6 +36631,8 @@ }, "strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "bundled": true, "requires": { "ansi-regex": "^5.0.1" @@ -36208,6 +36640,8 @@ }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "bundled": true, "requires": { "has-flag": "^4.0.0" @@ -36227,10 +36661,14 @@ }, "text-table": { "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "bundled": true }, "tiny-relative-date": { "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz", + "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==", "bundled": true }, "treeverse": { @@ -36239,6 +36677,8 @@ }, "unique-filename": { "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "bundled": true, "requires": { "unique-slug": "^2.0.0" @@ -36246,6 +36686,8 @@ }, "unique-slug": { "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "bundled": true, "requires": { "imurmurhash": "^0.1.4" @@ -36253,10 +36695,14 @@ }, "util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "bundled": true }, "validate-npm-package-license": { "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "bundled": true, "requires": { "spdx-correct": "^3.0.0", @@ -36265,6 +36711,8 @@ }, "validate-npm-package-name": { "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", "bundled": true, "requires": { "builtins": "^1.0.3" @@ -36272,10 +36720,14 @@ }, "walk-up-path": { "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/walk-up-path/-/walk-up-path-1.0.0.tgz", + "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==", "bundled": true }, "wcwidth": { "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "bundled": true, "requires": { "defaults": "^1.0.3" @@ -36283,6 +36735,8 @@ }, "which": { "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "bundled": true, "requires": { "isexe": "^2.0.0" @@ -36290,6 +36744,8 @@ }, "wide-align": { "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "bundled": true, "requires": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -36297,6 +36753,8 @@ }, "wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "bundled": true }, "write-file-atomic": { @@ -36309,6 +36767,8 @@ }, "yallist": { "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "bundled": true } } diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts index 71a45aadf..67f2c8c81 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts @@ -291,9 +291,14 @@ export class UserLoginComponent implements OnInit, OnDestroy { } else { // 清空路由复用信息 this.reuseTabService.clear(); - // 设置用户Token信息 - this.authnService.auth(res.data); - this.authnService.navigate({}); + if (res.data.twoFactor === '0') { + // 设置用户Token信息 + this.authnService.auth(res.data); + this.authnService.navigate({}); + } else { + localStorage.setItem(CONSTS.TWO_FACTOR_DATA, JSON.stringify(res.data)); + this.router.navigateByUrl('/passport/tfa'); + } } this.cdr.detectChanges(); }); diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport-routing.module.ts b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport-routing.module.ts index 3544e6d0f..77576d959 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport-routing.module.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport-routing.module.ts @@ -26,6 +26,7 @@ import { UserLoginComponent } from './login/login.component'; import { LogoutComponent } from './logout.component'; import { UserRegisterResultComponent } from './register-result/register-result.component'; import { UserRegisterComponent } from './register/register.component'; +import { TfaComponent } from './tfa/tfa.component'; const routes: Routes = [ // passport @@ -38,6 +39,11 @@ const routes: Routes = [ component: UserLoginComponent, data: { title: '登录', titleI18n: 'app.login.login' } }, + { + path: 'tfa', + component: TfaComponent, + data: { title: '登录二次认证', titleI18n: 'app.login.login' } + }, { path: 'register', component: UserRegisterComponent, diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport.module.ts b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport.module.ts index d012235ea..4b1d3f016 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport.module.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport.module.ts @@ -17,16 +17,26 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '@shared'; import { NzStepsModule } from 'ng-zorro-antd/steps'; + import { CallbackComponent } from './callback.component'; -import { SocialsProviderBindUserComponent } from './socials-provider-bind-user/socials-provider-bind-user.component'; import { ForgotComponent } from './forgot/forgot.component'; import { UserLockComponent } from './lock/lock.component'; import { UserLoginComponent } from './login/login.component'; import { PassportRoutingModule } from './passport-routing.module'; import { UserRegisterResultComponent } from './register-result/register-result.component'; import { UserRegisterComponent } from './register/register.component'; +import { SocialsProviderBindUserComponent } from './socials-provider-bind-user/socials-provider-bind-user.component'; +import { TfaComponent } from './tfa/tfa.component'; -const COMPONENTS = [SocialsProviderBindUserComponent,UserLoginComponent, UserRegisterResultComponent, UserRegisterComponent, UserLockComponent, CallbackComponent]; +const COMPONENTS = [ + TfaComponent, + SocialsProviderBindUserComponent, + UserLoginComponent, + UserRegisterResultComponent, + UserRegisterComponent, + UserLockComponent, + CallbackComponent +]; @NgModule({ imports: [SharedModule, PassportRoutingModule, NzStepsModule], diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.html b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.html new file mode 100644 index 000000000..c13ff4d68 --- /dev/null +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.html @@ -0,0 +1,101 @@ + +
+
+ + + + + + + + + + + + + + + + + + + {{ 'validation.phone-number.required' | i18n }} + + + {{ 'validation.phone-number.wrong-format' | i18n }} + + + + + + + + + + + + + + + + + + + + + + + {{ 'validation.phone-number.required' | i18n }} + + + {{ 'validation.phone-number.wrong-format' | i18n }} + + + + + + + + + + + + + + +
+ + + + {{ 'mxk.forgot.login' | i18n }} + + + + + +
diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.less b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.less new file mode 100644 index 000000000..00271eb6b --- /dev/null +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.less @@ -0,0 +1,75 @@ +@import '@delon/theme/index'; + +:host { + display: block; + width: 368px; + margin: 0 auto; + + + ::ng-deep { + .ant-tabs .ant-tabs-bar { + margin-bottom: 24px; + text-align: center; + border-bottom: 0; + } + + .ant-tabs-tab { + font-size: 16px; + line-height: 24px; + } + + .ant-input-affix-wrapper .ant-input:not(:first-child) { + padding-left: 4px; + } + + .login-tab { + color: #000; + background: unset; + border-color: unset; + border-right-color: unset; + } + + .icon { + margin-left: 16px; + color: rgb(0 0 0 / 20%); + font-size: 24px; + vertical-align: middle; + cursor: pointer; + transition: color 0.3s; + + &:hover { + color: @primary-color; + } + } + + .other { + margin-top: 24px; + line-height: 22px; + text-align: left; + + nz-tooltip { + vertical-align: middle; + } + + .register { + float: right; + } + } + } +} + +[data-theme='dark'] { + :host ::ng-deep { + .icon { + color: rgb(255 255 255 / 20%); + + &:hover { + color: #fff; + } + } + } +} + +input { + font-weight: bold; +} diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.spec.ts b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.spec.ts new file mode 100644 index 000000000..df967c6c2 --- /dev/null +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TfaComponent } from './tfa.component'; + +describe('TfaComponent', () => { + let component: TfaComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TfaComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TfaComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.ts b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.ts new file mode 100644 index 000000000..562f7e4a7 --- /dev/null +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/tfa/tfa.component.ts @@ -0,0 +1,212 @@ +/* + * Copyright [2025] [MaxKey of copyright http://www.maxkey.top] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + Inject, + inject, + OnInit, + OnDestroy, + AfterViewInit, + Optional +} from '@angular/core'; +import { AbstractControl, ReactiveFormsModule, FormBuilder, FormGroup, Validators, FormsModule } from '@angular/forms'; +import { Router, ActivatedRoute, RouterLink } from '@angular/router'; +import { ReuseTabService } from '@delon/abc/reuse-tab'; +import { SettingsService, _HttpClient, I18nPipe } from '@delon/theme'; +import { environment } from '@env/environment'; +import { NzAlertModule } from 'ng-zorro-antd/alert'; +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; +import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzFormModule } from 'ng-zorro-antd/form'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzRadioModule } from 'ng-zorro-antd/radio'; +import { NzTabChangeEvent, NzTabsModule } from 'ng-zorro-antd/tabs'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; +import { finalize } from 'rxjs/operators'; + +import { AuthnService } from '../../../service/authn.service'; +import { ImageCaptchaService } from '../../../service/image-captcha.service'; +import { CONSTS } from '../../../shared/consts'; + +@Component({ + selector: 'app-tfa', + templateUrl: './tfa.component.html', + styleUrls: ['./tfa.component.less'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class TfaComponent implements OnInit { + form: FormGroup; + error = ''; + secretKey = ''; + secretPublicKey = ''; + captchaType = ''; + twoFactorType = '0'; + twoFactorJwt = ''; + isFirstPasswordModify = 'N'; + state = ''; + defualtRedirectUri = ''; + count = 0; + interval$: any; + loading = false; + constructor( + fb: FormBuilder, + private router: Router, + private settingsService: SettingsService, + private authnService: AuthnService, + private imageCaptchaService: ImageCaptchaService, + private route: ActivatedRoute, + private msg: NzMessageService, + @Optional() + @Inject(ReuseTabService) + private reuseTabService: ReuseTabService, + private cdr: ChangeDetectorRef + ) { + this.form = fb.group({ + userName: [null, [Validators.required]], + password: [null, [Validators.required]], + captcha: [null, [Validators.required]], + mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]], + twoFactorMobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]], + twoFactorEmail: [null, [Validators.required, Validators.pattern(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/)]], + otpCaptcha: [null, [Validators.required]], + remember: [false] + }); + } + + ngOnInit(): void { + this.authnService + .get({ remember_me: localStorage.getItem(CONSTS.REMEMBER) }) + .pipe( + finalize(() => { + this.loading = false; + this.cdr.detectChanges(); + }) + ) + .subscribe(res => { + this.loading = true; + if (res.code !== 0) { + this.error = res.msg; + } else { + this.state = res.data.state; + this.defualtRedirectUri = res.data.redirectUri; + this.captchaType = res.data.captcha; + this.secretKey = res.data.secretKey; + this.secretPublicKey = res.data.secretPublicKey; + this.isFirstPasswordModify = res.data.isFirstPasswordModify; + } + }); + let twoFactorData = JSON.parse(localStorage.getItem(CONSTS.TWO_FACTOR_DATA) || ''); + this.twoFactorType = twoFactorData.twoFactor; + this.twoFactorJwt = twoFactorData.token; + this.twoFactorMobile.setValue(twoFactorData.mobile); + this.twoFactorEmail.setValue(twoFactorData.email); + } + + get userName(): AbstractControl { + return this.form.get('userName')!; + } + get password(): AbstractControl { + return this.form.get('password')!; + } + get mobile(): AbstractControl { + return this.form.get('mobile')!; + } + get captcha(): AbstractControl { + return this.form.get('captcha')!; + } + + get otpCaptcha(): AbstractControl { + return this.form.get('otpCaptcha')!; + } + + get remember(): AbstractControl { + return this.form.get('remember')!; + } + + get twoFactorMobile(): AbstractControl { + return this.form.get('twoFactorMobile')!; + } + + get twoFactorEmail(): AbstractControl { + return this.form.get('twoFactorEmail')!; + } + + sendTwoFactorOtpCode(): void { + this.authnService.sendTwoFactorCode({ jwtToken: this.twoFactorJwt }).subscribe(res => { + if (res.code !== 0) { + this.msg.success(`发送失败`); + } + }); + this.count = 59; + this.interval$ = setInterval(() => { + this.count -= 1; + if (this.count <= 0) { + clearInterval(this.interval$); + } + this.cdr.detectChanges(); + }, 1000); + } + + submit(): void { + this.error = ''; + + this.otpCaptcha.markAsDirty(); + this.otpCaptcha.updateValueAndValidity(); + + localStorage.setItem(CONSTS.REMEMBER, this.form.get(CONSTS.REMEMBER)?.value); + + this.loading = true; + this.cdr.detectChanges(); + this.authnService + .login({ + authType: 'twoFactor', + state: this.state, + jwtToken: this.twoFactorJwt, + otpCaptcha: this.otpCaptcha.value, + remeberMe: this.remember.value + }) + .pipe( + finalize(() => { + this.loading = false; + this.cdr.detectChanges(); + }) + ) + .subscribe(res => { + this.loading = true; + if (res.code !== 0) { + this.error = res.msg; + } else { + localStorage.removeItem(CONSTS.TWO_FACTOR_DATA); + // 清空路由复用信息 + this.reuseTabService?.clear(); + // 设置用户Token信息 + this.authnService.auth(res.data); + this.authnService.navigate({ + defualtRedirectUri: this.defualtRedirectUri, + isFirstPasswordModify: this.isFirstPasswordModify, + passwordSetType: res.data.passwordSetType + }); + } + this.cdr.detectChanges(); + }); + } +} diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/service/authn.service.ts b/maxkey-web-frontend/maxkey-web-app/src/app/service/authn.service.ts index 315ad27f4..abff1f2d1 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/service/authn.service.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/service/authn.service.ts @@ -61,6 +61,10 @@ export class AuthnService { return this.http.post('/login/signin?_allow_anonymous=true', authParam); } + sendTwoFactorCode(authParam: any) { + return this.http.post(`/login/sendTwoFactorCode?_allow_anonymous=true`, authParam); + } + bindSocialsUser(authParam: any) { return this.http.post('/login/signin/bindusersocials?_allow_anonymous=true', authParam); } diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts b/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts index bef86e68f..42efad6cc 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts @@ -22,6 +22,7 @@ export const CONSTS = { /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi, INST: 'inst', CONGRESS: 'congress', + TWO_FACTOR_DATA: 'two_factor_data', ONLINE_TICKET: 'online_ticket', REDIRECT_URI: 'redirect_uri', REMEMBER: 'remember_me', diff --git a/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/en-US.json b/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/en-US.json index 03c1ec2f2..1f7060e10 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/en-US.json +++ b/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/en-US.json @@ -15,6 +15,7 @@ "sign-in-with": "Sign in with", "signup": "Sign up", "login": "Login", + "twoFactor": "2-Factor Authentication", "text.username": "Username", "text.mobile": "Mobile Number", "text.password": "Password", diff --git a/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-CN.json b/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-CN.json index 96f8171d3..014e06a62 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-CN.json +++ b/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-CN.json @@ -15,6 +15,7 @@ "sign-in-with": "其他登录方式", "signup": "用户注册", "login": "登录", + "twoFactor": "二次身份认证", "text.username": "用户名", "text.mobile": "手机号码", "text.password": "密码", diff --git a/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-TW.json b/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-TW.json index baa565e0b..7f80af38a 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-TW.json +++ b/maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-TW.json @@ -15,6 +15,7 @@ "sign-in-with": "其他登錄方式", "signup": "用戶註冊", "login": "登錄", + "twoFactor": "二次身份认证", "text.username": "用戶名", "text.mobile": "手機號碼", "text.password": "密碼",