Merge pull request #1068 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
		
						commit
						fd0487c04f
					
				@ -71,11 +71,7 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({
 | 
			
		||||
export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) {
 | 
			
		||||
  const params = [ `stream=${stream}` ];
 | 
			
		||||
 | 
			
		||||
  if (accessToken !== null) {
 | 
			
		||||
    params.push(`access_token=${accessToken}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`);
 | 
			
		||||
  const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`, accessToken);
 | 
			
		||||
 | 
			
		||||
  ws.onopen      = connected;
 | 
			
		||||
  ws.onmessage   = e => received(JSON.parse(e.data));
 | 
			
		||||
 | 
			
		||||
@ -163,21 +163,28 @@ class ColumnsArea extends ImmutablePureComponent {
 | 
			
		||||
    if (singleColumn) {
 | 
			
		||||
      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
 | 
			
		||||
 | 
			
		||||
      return columnIndex !== -1 ? [
 | 
			
		||||
        <TabsBar key='tabs' />,
 | 
			
		||||
 | 
			
		||||
      const content = columnIndex !== -1 ? (
 | 
			
		||||
        <ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
 | 
			
		||||
          {links.map(this.renderView)}
 | 
			
		||||
        </ReactSwipeableViews>,
 | 
			
		||||
        </ReactSwipeableViews>
 | 
			
		||||
      ) : (
 | 
			
		||||
        <div key='content' className='columns-area columns-area--mobile'>{children}</div>
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
        floatingActionButton,
 | 
			
		||||
      ] : [
 | 
			
		||||
        <TabsBar key='tabs' />,
 | 
			
		||||
      return (
 | 
			
		||||
        <div className='columns-area__panels'>
 | 
			
		||||
          <div className='columns-area__panels__pane' />
 | 
			
		||||
 | 
			
		||||
        <div key='content' className='columns-area columns-area--mobile'>{children}</div>,
 | 
			
		||||
          <div className='columns-area__panels__main'>
 | 
			
		||||
            <TabsBar key='tabs' />
 | 
			
		||||
            {content}
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
        floatingActionButton,
 | 
			
		||||
      ];
 | 
			
		||||
          <div className='columns-area__panels__pane' />
 | 
			
		||||
 | 
			
		||||
          {floatingActionButton}
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
 | 
			
		||||
@ -71,11 +71,7 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({
 | 
			
		||||
export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) {
 | 
			
		||||
  const params = [ `stream=${stream}` ];
 | 
			
		||||
 | 
			
		||||
  if (accessToken !== null) {
 | 
			
		||||
    params.push(`access_token=${accessToken}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`);
 | 
			
		||||
  const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`, accessToken);
 | 
			
		||||
 | 
			
		||||
  ws.onopen      = connected;
 | 
			
		||||
  ws.onmessage   = e => received(JSON.parse(e.data));
 | 
			
		||||
 | 
			
		||||
@ -1786,6 +1786,39 @@ a.account__display-name {
 | 
			
		||||
  &.unscrollable {
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__panels {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
 | 
			
		||||
    &__pane {
 | 
			
		||||
      flex: 1 1 auto;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
      pointer-events: none;
 | 
			
		||||
      display: flex;
 | 
			
		||||
      justify-content: flex-end;
 | 
			
		||||
 | 
			
		||||
      &__inner {
 | 
			
		||||
        pointer-events: auto;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__main {
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      max-width: 600px;
 | 
			
		||||
      display: flex;
 | 
			
		||||
      flex-direction: column;
 | 
			
		||||
 | 
			
		||||
      @media screen and (min-width: 360px) {
 | 
			
		||||
        padding: 0 10px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.react-swipeable-view-container {
 | 
			
		||||
@ -1936,7 +1969,6 @@ a.account__display-name {
 | 
			
		||||
.columns-area--mobile {
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  max-width: 600px;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
 | 
			
		||||
  .column,
 | 
			
		||||
@ -1952,7 +1984,7 @@ a.account__display-name {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media screen and (min-width: 360px) {
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    padding: 10px 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media screen and (min-width: 630px) {
 | 
			
		||||
@ -2013,8 +2045,7 @@ a.account__display-name {
 | 
			
		||||
  .tabs-bar {
 | 
			
		||||
    margin: 10px auto;
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
    width: calc(100% - 20px);
 | 
			
		||||
    max-width: 600px;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .react-swipeable-view-container .columns-area--mobile {
 | 
			
		||||
@ -5427,6 +5458,10 @@ noscript {
 | 
			
		||||
  &:active {
 | 
			
		||||
    background: lighten($ui-highlight-color, 7%);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media screen and (min-width: 630px) {
 | 
			
		||||
    display: none;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.account__header__content {
 | 
			
		||||
 | 
			
		||||
@ -195,14 +195,14 @@ const startWorker = (workerId) => {
 | 
			
		||||
    next();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const accountFromToken = (token, req, next) => {
 | 
			
		||||
  const accountFromToken = (token, allowedScopes, req, next) => {
 | 
			
		||||
    pgPool.connect((err, client, done) => {
 | 
			
		||||
      if (err) {
 | 
			
		||||
        next(err);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      client.query('SELECT oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token], (err, result) => {
 | 
			
		||||
      client.query('SELECT oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token], (err, result) => {
 | 
			
		||||
        done();
 | 
			
		||||
 | 
			
		||||
        if (err) {
 | 
			
		||||
@ -218,18 +218,29 @@ const startWorker = (workerId) => {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const scopes = result.rows[0].scopes.split(' ');
 | 
			
		||||
 | 
			
		||||
        if (allowedScopes.size > 0 && !scopes.some(scope => allowedScopes.includes(scope))) {
 | 
			
		||||
          err = new Error('Access token does not cover required scopes');
 | 
			
		||||
          err.statusCode = 401;
 | 
			
		||||
 | 
			
		||||
          next(err);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        req.accountId = result.rows[0].account_id;
 | 
			
		||||
        req.chosenLanguages = result.rows[0].chosen_languages;
 | 
			
		||||
        req.allowNotifications = scopes.some(scope => ['read', 'read:notifications'].includes(scope));
 | 
			
		||||
 | 
			
		||||
        next();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const accountFromRequest = (req, next, required = true) => {
 | 
			
		||||
  const accountFromRequest = (req, next, required = true, allowedScopes = ['read']) => {
 | 
			
		||||
    const authorization = req.headers.authorization;
 | 
			
		||||
    const location = url.parse(req.url, true);
 | 
			
		||||
    const accessToken = location.query.access_token;
 | 
			
		||||
    const accessToken = location.query.access_token || req.headers['sec-websocket-protocol'];
 | 
			
		||||
 | 
			
		||||
    if (!authorization && !accessToken) {
 | 
			
		||||
      if (required) {
 | 
			
		||||
@ -246,7 +257,7 @@ const startWorker = (workerId) => {
 | 
			
		||||
 | 
			
		||||
    const token = authorization ? authorization.replace(/^Bearer /, '') : accessToken;
 | 
			
		||||
 | 
			
		||||
    accountFromToken(token, req, next);
 | 
			
		||||
    accountFromToken(token, allowedScopes, req, next);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const PUBLIC_STREAMS = [
 | 
			
		||||
@ -261,6 +272,16 @@ const startWorker = (workerId) => {
 | 
			
		||||
  const wsVerifyClient = (info, cb) => {
 | 
			
		||||
    const location = url.parse(info.req.url, true);
 | 
			
		||||
    const authRequired = !PUBLIC_STREAMS.some(stream => stream === location.query.stream);
 | 
			
		||||
    const allowedScopes = [];
 | 
			
		||||
 | 
			
		||||
    if (authRequired) {
 | 
			
		||||
      allowedScopes.push('read');
 | 
			
		||||
      if (location.query.stream === 'user:notification') {
 | 
			
		||||
        allowedScopes.push('read:notifications');
 | 
			
		||||
      } else {
 | 
			
		||||
        allowedScopes.push('read:statuses');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    accountFromRequest(info.req, err => {
 | 
			
		||||
      if (!err) {
 | 
			
		||||
@ -269,7 +290,7 @@ const startWorker = (workerId) => {
 | 
			
		||||
        log.error(info.req.requestId, err.toString());
 | 
			
		||||
        cb(false, 401, 'Unauthorized');
 | 
			
		||||
      }
 | 
			
		||||
    }, authRequired);
 | 
			
		||||
    }, authRequired, allowedScopes);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const PUBLIC_ENDPOINTS = [
 | 
			
		||||
@ -286,7 +307,18 @@ const startWorker = (workerId) => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const authRequired = !PUBLIC_ENDPOINTS.some(endpoint => endpoint === req.path);
 | 
			
		||||
    accountFromRequest(req, next, authRequired);
 | 
			
		||||
    const allowedScopes = [];
 | 
			
		||||
 | 
			
		||||
    if (authRequired) {
 | 
			
		||||
      allowedScopes.push('read');
 | 
			
		||||
      if (req.path === '/api/v1/streaming/user/notification') {
 | 
			
		||||
        allowedScopes.push('read:notifications');
 | 
			
		||||
      } else {
 | 
			
		||||
        allowedScopes.push('read:statuses');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    accountFromRequest(req, next, authRequired, allowedScopes);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const errorMiddleware = (err, req, res, {}) => {
 | 
			
		||||
@ -339,6 +371,10 @@ const startWorker = (workerId) => {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (event === 'notification' && !req.allowNotifications) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Only send local-only statuses to logged-in users
 | 
			
		||||
      if (payload.local_only && !req.accountId) {
 | 
			
		||||
        log.silly(req.requestId, `Message ${payload.id} filtered because it was local-only`);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user