Merge pull request #230 from hyperspacedev/feat/check-bypass

Added instance name validation bypass
This commit is contained in:
Marquis Kurt 2020-10-03 13:31:54 -04:00 committed by GitHub
commit 07be3af687
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 61 deletions

View File

@ -20,7 +20,7 @@ import {
DialogContent, DialogContent,
DialogContentText, DialogContentText,
DialogActions, DialogActions,
TextField, TextField
} from "@material-ui/core"; } from "@material-ui/core";
import { withSnackbar } from "notistack"; import { withSnackbar } from "notistack";
@ -53,7 +53,7 @@ class Blocked extends Component<any, IBlockedState> {
viewIsLoading: true, viewIsLoading: true,
viewDidLoad: false, viewDidLoad: false,
viewDidError: false, viewDidError: false,
blockTextField: "", blockTextField: ""
}; };
} }
@ -64,14 +64,14 @@ class Blocked extends Component<any, IBlockedState> {
this.setState({ this.setState({
blockedServers: resp.data, blockedServers: resp.data,
viewDidLoad: true, viewDidLoad: true,
viewIsLoading: false, viewIsLoading: false
}); });
}) })
.catch((err: Error) => { .catch((err: Error) => {
console.error(err); console.error(err);
this.setState({ this.setState({
viewIsLoading: false, viewIsLoading: false,
viewDidError: true, viewDidError: true
}); });
}); });
} }
@ -90,7 +90,7 @@ class Blocked extends Component<any, IBlockedState> {
this.setState({ this.setState({
blockTextField: "", blockTextField: "",
addBlockOpen: false, addBlockOpen: false,
blockedServers, blockedServers
}); });
}); });
} }
@ -105,7 +105,7 @@ class Blocked extends Component<any, IBlockedState> {
blockedServers.splice(blockedServers.indexOf(domain), 1); blockedServers.splice(blockedServers.indexOf(domain), 1);
} }
this.setState({ this.setState({
blockedServers, blockedServers
}); });
}) })
.catch((err: Error) => { .catch((err: Error) => {
@ -118,7 +118,7 @@ class Blocked extends Component<any, IBlockedState> {
updateTextField(value: string) { updateTextField(value: string) {
this.setState({ this.setState({
blockTextField: value, blockTextField: value
}); });
} }
@ -140,7 +140,7 @@ class Blocked extends Component<any, IBlockedState> {
fullWidth fullWidth
value={this.state.blockTextField} value={this.state.blockTextField}
placeholder="mastodon.online" placeholder="mastodon.online"
onChange={(e) => this.updateTextField(e.target.value)} onChange={e => this.updateTextField(e.target.value)}
></TextField> ></TextField>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
@ -153,9 +153,7 @@ class Blocked extends Component<any, IBlockedState> {
</Button> </Button>
<Button <Button
color="primary" color="primary"
onClick={(e) => onClick={e => this.addBlock(this.state.blockTextField)}
this.addBlock(this.state.blockTextField)
}
> >
Add Add
</Button> </Button>

View File

@ -19,7 +19,7 @@ import {
ListItemAvatar, ListItemAvatar,
ListItemSecondaryAction, ListItemSecondaryAction,
IconButton, IconButton,
InputAdornment, InputAdornment
} from "@material-ui/core"; } from "@material-ui/core";
import { styles } from "./WelcomePage.styles"; import { styles } from "./WelcomePage.styles";
import Mastodon from "megalodon"; import Mastodon from "megalodon";
@ -28,7 +28,7 @@ import {
createHyperspaceApp, createHyperspaceApp,
getRedirectAddress, getRedirectAddress,
inDisallowedDomains, inDisallowedDomains,
instancesBearerKey, instancesBearerKey
} from "../utilities/login"; } from "../utilities/login";
import { parseUrl } from "query-string"; import { parseUrl } from "query-string";
import { getConfig } from "../utilities/settings"; import { getConfig } from "../utilities/settings";
@ -39,7 +39,7 @@ import { Config } from "../types/Config";
import { import {
getAccountRegistry, getAccountRegistry,
loginWithAccount, loginWithAccount,
removeAccountFromRegistry, removeAccountFromRegistry
} from "../utilities/accounts"; } from "../utilities/accounts";
import { MultiAccount } from "../types/Account"; import { MultiAccount } from "../types/Account";
@ -210,7 +210,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
authCode: "", authCode: "",
emergencyMode: false, emergencyMode: false,
version: "", version: "",
willAddAccount: false, willAddAccount: false
}; };
// Read the configuration data and update the state // Read the configuration data and update the state
@ -253,7 +253,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
? config.location ? config.location
: `https://${window.location.host}`, : `https://${window.location.host}`,
redirectAddressIsDynamic: config.location === "dynamic", redirectAddressIsDynamic: config.location === "dynamic",
version: config.version, version: config.version
}); });
} }
}) })
@ -274,7 +274,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
if (localStorage.getItem("login")) { if (localStorage.getItem("login")) {
this.getSavedSession(); this.getSavedSession();
this.setState({ this.setState({
foundSavedLogin: true, foundSavedLogin: true
}); });
this.checkForToken(); this.checkForToken();
} }
@ -334,7 +334,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
clientId: session.clientId, clientId: session.clientId,
clientSecret: session.clientSecret, clientSecret: session.clientSecret,
authUrl: session.authUrl, authUrl: session.authUrl,
emergencyMode: session.emergency, emergencyMode: session.emergency
}); });
} }
@ -392,8 +392,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
this.setState({ user: newUser }); this.setState({ user: newUser });
return "https://" + newUser.split("@")[1]; return "https://" + newUser.split("@")[1];
} else { } else {
let newUser = `${user}@${this.state.registerBase ?? "mastodon.online" let newUser = `${user}@${this.state.registerBase ??
}`; "mastodon.online"}`;
this.setState({ user: newUser }); this.setState({ user: newUser });
return ( return (
"https://" + (this.state.registerBase ?? "mastodon.online") "https://" + (this.state.registerBase ?? "mastodon.online")
@ -403,8 +403,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
// Otherwise, treat them as if they're from the server // Otherwise, treat them as if they're from the server
else { else {
let newUser = `${user}@${this.state.registerBase ?? "mastodon.online" let newUser = `${user}@${this.state.registerBase ??
}`; "mastodon.online"}`;
this.setState({ user: newUser }); this.setState({ user: newUser });
return "https://" + (this.state.registerBase ?? "mastodon.online"); return "https://" + (this.state.registerBase ?? "mastodon.online");
} }
@ -413,10 +413,11 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
/** /**
* Check the user string for any errors and then create a client with an * Check the user string for any errors and then create a client with an
* ID and secret to start the authorization process. * ID and secret to start the authorization process.
* @param bypassChecks Whether to bypass the checks in place.
*/ */
startLogin() { startLogin(bypassChecks: boolean = false) {
// Check if we have errored // Check if we have errored
let error = this.checkForErrors(this.state.user); let error = this.checkForErrors(this.state.user, bypassChecks);
// If we didn't, create the Hyperspace app to register onto that Mastodon // If we didn't, create the Hyperspace app to register onto that Mastodon
// server. // server.
@ -439,7 +440,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
clientId: resp.clientId, clientId: resp.clientId,
clientSecret: resp.clientSecret, clientSecret: resp.clientSecret,
authUrl: resp.url, authUrl: resp.url,
emergency: false, emergency: false
}; };
localStorage.setItem( localStorage.setItem(
"login", "login",
@ -451,8 +452,17 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
clientId: resp.clientId, clientId: resp.clientId,
clientSecret: resp.clientSecret, clientSecret: resp.clientSecret,
authUrl: resp.url, authUrl: resp.url,
proceedToGetCode: true, proceedToGetCode: true
}); });
})
.catch((err: Error) => {
this.props.enqueueSnackbar(
`Failed to register app at ${baseurl.replace(
"https://",
""
)}`
);
console.error(err);
}); });
} }
} }
@ -475,7 +485,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
Mastodon.registerApp( Mastodon.registerApp(
this.state.brandName ?? "Hyperspace", this.state.brandName ?? "Hyperspace",
{ {
scopes: scopes, scopes: scopes
}, },
baseurl baseurl
) )
@ -485,7 +495,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
clientId: appData.clientId, clientId: appData.clientId,
clientSecret: appData.clientSecret, clientSecret: appData.clientSecret,
authUrl: appData.url, authUrl: appData.url,
emergency: true, emergency: true
}; };
localStorage.setItem( localStorage.setItem(
"login", "login",
@ -496,7 +506,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
this.setState({ this.setState({
clientId: appData.clientId, clientId: appData.clientId,
clientSecret: appData.clientSecret, clientSecret: appData.clientSecret,
authUrl: appData.url, authUrl: appData.url
}); });
}); });
} }
@ -528,15 +538,21 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
clientSecret: session.clientSecret, clientSecret: session.clientSecret,
authUrl: session.authUrl, authUrl: session.authUrl,
emergencyMode: session.emergency, emergencyMode: session.emergency,
proceedToGetCode: true, proceedToGetCode: true
}); });
} }
} }
/** /**
* Check the user input string for any possible errors * Check the user input string for any possible errors
* @param username The username to read and check for errors
* @param bypassesInstanceNameCheck Whether to bypass the instance name validation process. Defaults to false.
* @return Whether an error has occured in the validation.
*/ */
checkForErrors(username: string): boolean { checkForErrors(
username: string,
bypassesInstanceNameCheck: boolean = false
): boolean {
let userInputError = false; let userInputError = false;
let userInputErrorMessage = ""; let userInputErrorMessage = "";
@ -555,28 +571,31 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
if (inDisallowedDomains(baseUrl)) { if (inDisallowedDomains(baseUrl)) {
this.setState({ this.setState({
userInputError: true, userInputError: true,
userInputErrorMessage: `Signing in with an account from ${baseUrl} isn't supported.`, userInputErrorMessage: `Signing in with an account from ${baseUrl} isn't supported.`
}); });
return true; return true;
} else { } else {
if (bypassesInstanceNameCheck) {
return false;
}
// Are we unable to ping the server? // Are we unable to ping the server?
axios axios
.get( .get(
"https://instances.social/api/1.0/instances/show?name=" + "https://instances.social/api/1.0/instances/show?name=" +
baseUrl, baseUrl,
{ {
headers: { headers: {
Authorization: `Bearer ${instancesBearerKey}`, Authorization: `Bearer ${instancesBearerKey}`
}, }
} }
) )
.catch((err: Error) => { .catch((err: Error) => {
let userInputError = true; let userInputError = true;
let userInputErrorMessage = let userInputErrorMessage =
"Instance name is invalid."; "We couldn't recognize this instance.";
this.setState({ this.setState({
userInputError, userInputError,
userInputErrorMessage, userInputErrorMessage
}); });
return true; return true;
}); });
@ -633,9 +652,9 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
let redirectUrl: string | undefined = let redirectUrl: string | undefined =
this.state.emergencyMode || this.state.emergencyMode ||
clientLoginSession.authUrl.includes( clientLoginSession.authUrl.includes(
"urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob" "urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob"
) )
? undefined ? undefined
: getRedirectAddress(conf.location); : getRedirectAddress(conf.location);
@ -658,8 +677,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
}) })
.catch((err: Error) => { .catch((err: Error) => {
this.props.enqueueSnackbar( this.props.enqueueSnackbar(
`Couldn't authorize ${this.state.brandName ?? "Hyperspace" `Couldn't authorize ${this.state.brandName ??
}: ${err.name}`, "Hyperspace"}: ${err.name}`,
{ variant: "error" } { variant: "error" }
); );
console.error(err.message); console.error(err.message);
@ -677,8 +696,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
window.location.protocol === "hyperspace:" window.location.protocol === "hyperspace:"
? "hyperspace://hyperspace/app/" ? "hyperspace://hyperspace/app/"
: this.state.redirectAddressIsDynamic : this.state.redirectAddressIsDynamic
? `https://${window.location.host}/#/` ? `https://${window.location.host}/#/`
: this.state.defaultRedirectAddress + "/#/"; : this.state.defaultRedirectAddress + "/#/";
} }
/** /**
@ -770,20 +789,30 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
label="Username" label="Username"
fullWidth fullWidth
placeholder="example@mastodon.example" placeholder="example@mastodon.example"
onChange={(event) => onChange={event => this.updateUserInfo(event.target.value)}
this.updateUserInfo(event.target.value) onKeyDown={event => this.watchUsernameField(event)}
}
onKeyDown={(event) => this.watchUsernameField(event)}
error={this.state.userInputError} error={this.state.userInputError}
InputProps={{ InputProps={{
startAdornment: ( startAdornment: (
<InputAdornment position="start">@</InputAdornment> <InputAdornment position="start">@</InputAdornment>
), )
}} }}
/> />
{this.state.userInputError ? ( {this.state.userInputError ? (
<Typography color="error"> <Typography color="error">
{this.state.userInputErrorMessage} {this.state.userInputErrorMessage}
{this.state.userInputErrorMessage ===
"We couldn't recognize this instance." ? (
<span>
<br />
<Link
// className={classes.welcomeLink}
onClick={() => this.startLogin(true)}
>
Try anyway
</Link>
</span>
) : null}
</Typography> </Typography>
) : null} ) : null}
<br /> <br />
@ -919,10 +948,10 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
variant="outlined" variant="outlined"
label="Authorization code" label="Authorization code"
fullWidth fullWidth
onChange={(event) => onChange={event =>
this.updateAuthCode(event.target.value) this.updateAuthCode(event.target.value)
} }
onKeyDown={(event) => this.watchAuthField(event)} onKeyDown={event => this.watchAuthField(event)}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
@ -974,8 +1003,8 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
<div <div
className={classes.root} className={classes.root}
style={{ style={{
backgroundImage: `url(${this.state.backgroundUrl ?? "background.png" backgroundImage: `url(${this.state.backgroundUrl ??
})`, "background.png"})`
}} }}
> >
<Paper className={classes.paper}> <Paper className={classes.paper}>
@ -989,17 +1018,17 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
{this.state.authorizing {this.state.authorizing
? this.showAuthorizationLoader() ? this.showAuthorizationLoader()
: this.state.proceedToGetCode : this.state.proceedToGetCode
? this.showLoginAuth() ? this.showLoginAuth()
: getAccountRegistry().length > 0 && : getAccountRegistry().length > 0 &&
!this.state.willAddAccount !this.state.willAddAccount
? this.showMultiAccount() ? this.showMultiAccount()
: this.showLanding()} : this.showLanding()}
</Fade> </Fade>
<br /> <br />
<Typography variant="caption"> <Typography variant="caption">
&copy; {new Date().getFullYear()}{" "} &copy; {new Date().getFullYear()}{" "}
{this.state.brandName && {this.state.brandName &&
this.state.brandName !== "Hyperspace" this.state.brandName !== "Hyperspace"
? `${this.state.brandName} developers and the ` ? `${this.state.brandName} developers and the `
: ""}{" "} : ""}{" "}
<Link <Link
@ -1040,7 +1069,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
> >
License License
</Link>{" "} </Link>{" "}
| |{" "}
<Link <Link
className={classes.welcomeLink} className={classes.welcomeLink}
href="https://github.com/hyperspacedev/hyperspace/issues/new" href="https://github.com/hyperspacedev/hyperspace/issues/new"
@ -1054,7 +1083,7 @@ class WelcomePage extends Component<IWelcomeProps, IWelcomeState> {
{this.state.brandName ?? "Hypersapce"} v. {this.state.brandName ?? "Hypersapce"} v.
{this.state.version}{" "} {this.state.version}{" "}
{this.state.brandName && {this.state.brandName &&
this.state.brandName !== "Hyperspace" this.state.brandName !== "Hyperspace"
? "(Hyperspace-like)" ? "(Hyperspace-like)"
: null} : null}
</Typography> </Typography>