Skip to content

Commit

Permalink
Initial run at adding a SVN Password functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
dd32 committed Jul 4, 2024
1 parent c272f12 commit 902f846
Show file tree
Hide file tree
Showing 9 changed files with 2,631 additions and 2,784 deletions.
2 changes: 1 addition & 1 deletion settings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@wordpress/core-data": "^6.4.0",
"@wordpress/data": "^8.4.0",
"@wordpress/element": "^5.4.0",
"@wordpress/icons": "^9.18.0",
"@wordpress/icons": "^9.49.0",
"@wordpress/scripts": "^27.6.0",
"lodash": "^4.17.21"
}
Expand Down
98 changes: 98 additions & 0 deletions settings/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,104 @@ function register_user_fields(): void {
]
);

register_rest_field(
'user',
'svn_password_required',
[
'get_callback' => function( $user ) {
global $wpdb;

$user = get_userdata( $user['id'] );
if ( ! $user ) {
return false;
}

// Committers, supes, etc.
if ( function_exists( 'is_special_user' ) && is_special_user( $user->ID ) ) {
return true;
}

// TODO: These should be set as a user flag.

// Plugin committers.
$plugin_committer = (bool) $wpdb->get_var( $wpdb->prepare(
'SELECT 1 FROM `' . PLUGINS_TABLE_PREFIX . 'svn_access` WHERE user = %s',
$user->user_login
) );
if ( $plugin_committer ) {
return true;
}

// Theme Authors, don't _need_ SVN access.
$theme_author = (bool) $wpdb->get_var( $wpdb->prepare(
'SELECT 1 FROM `wporg_' . WPORG_THEME_DIRECTORY_BLOGID . '_posts` ' .
'WHERE post_type = "repopackage" AND post_status = "publish" AND post_author = %d',
$user->ID
) );
if ( $theme_author ) {
return true;
}

return false;
},
'schema' => [
'type' => 'boolean',
'context' => [ 'edit' ],
]
]
);

$regenerated_password = '';
register_rest_field(
'user',
'svn_password',
[
'get_callback' => function( $user ) use( &$regenerated_password ) {
global $wpdb;

// If the password was just generated, return it in response.
if ( $regenerated_password ) {
return $regenerated_password;
}

// TODO, cache
return (bool) $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM wporg_svn_auth WHERE ID = %d AND `type` = 'svn' LIMIT 1",
$user['id']
) );
},
'update_callback' => function( $value, $user ) use( &$regenerated_password ) {
global $wpdb;
if ( 'regenerate' !== $value ) {
return false;
}

$regenerated_password = wp_generate_password( 40, false );
$hashed_password = wp_hash_password( $regenerated_password );

$row = [
'ID' => $user->ID,
'user_login' => $user->user_login,
'svn_pass' => $hashed_password,
'type' => 'svn',
'active' => 1,
];

// TODO, what else needs doing here.
$inserted = $wpdb->update( 'wporg_svn_auth', $row, [ 'ID' => $user->ID ] );
if ( ! $inserted ) {
$inserted = $wpdb->insert( 'wporg_svn_auth', $row );
}

return (bool) $inserted;
},
'schema' => [
'type' => [ 'boolean', 'string' ],
'context' => [ 'edit' ],
]
]
);

}

/**
Expand Down
28 changes: 27 additions & 1 deletion settings/src/components/account-status.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export default function AccountStatus() {
const {
user: {
userRecord: {
record: { email, pending_email: pendingEmail },
record: {

Check failure on line 21 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Delete `·`
email,
pending_email: pendingEmail,
svn_password: svnPasswordSet,
svn_password_required: svnPasswordRequired,
},
},
hasPrimaryProvider,
primaryProvider,
Expand Down Expand Up @@ -87,6 +92,27 @@ export default function AccountStatus() {
bodyText={ backupBodyText }
disabled={ ! hasPrimaryProvider }
/>

{

Check failure on line 96 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Replace `⏎↹↹↹↹(·svnPasswordRequired·||·svnPasswordSet·)·?` with `·svnPasswordRequired·||·svnPasswordSet·?·(`
( svnPasswordRequired || svnPasswordSet ) ?
<SettingStatusCard

Check failure on line 98 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Delete `↹`
screen="svn-password"

Check failure on line 99 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Delete `↹`
status={ ! svnPasswordRequired && ! svnPasswordSet ? 'info' : !! svnPasswordSet }

Check failure on line 100 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Replace `↹status={·!·svnPasswordRequired·&&·!·svnPasswordSet·?·'info'·:·!!·svnPasswordSet·}` with `status={⏎↹↹↹↹↹↹!·svnPasswordRequired·&&·!·svnPasswordSet·?·'info'·:·!!·svnPasswordSet`
headerText="SVN Password"

Check failure on line 101 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Insert `}⏎↹↹↹↹`
bodyText={

Check failure on line 102 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Delete `↹`
svnPasswordRequired ? (

Check failure on line 103 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Replace `↹↹↹↹↹↹↹svnPasswordRequired·?·(` with `↹↹↹↹↹↹svnPasswordRequired`

Check failure on line 103 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Do not nest ternary expressions
svnPasswordSet ?

Check failure on line 104 in settings/src/components/account-status.js

View workflow job for this annotation

GitHub Actions / All

Replace `↹svnPasswordSet·?` with `?·svnPasswordSet`
'You have an active SVN password set. You can request a new one at any time.' :
'You have not set up an SVN password. This is required for SVN access to WordPress.org.'
) : (
svnPasswordSet ?
'You have an active SVN password set. You can request a new one at any time.' :
'You do not currently require WordPress.org SVN access.'
)
}
/>
: ''
}
</div>
);
}
Expand Down
3 changes: 2 additions & 1 deletion settings/src/components/screen-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const ScreenNavigation = ( { screen, children } ) => (
{ screen
.replace( '-', ' ' )
.replace( 'totp', 'Two-Factor Authentication' )
.replace( 'webauthn', 'Two-Factor Security Key' ) }
.replace( 'webauthn', 'Two-Factor Security Key' )
.replace( 'svn', 'SVN' ) }
</h3>
</CardHeader>
<CardBody className={ 'wporg-2fa__' + screen }>{ children }</CardBody>
Expand Down
79 changes: 79 additions & 0 deletions settings/src/components/svn-password.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* WordPress dependencies
*/
import { Button, TextControl, Notice, Spinner } from '@wordpress/components';
import { useCallback, useContext, useState } from '@wordpress/element';
import { Icon, check, copySmall } from '@wordpress/icons';

/**
* Internal dependencies
*/
import { GlobalContext } from '../script';

/**
* Render the Email setting.
*/
export default function SVNPassword() {
const {
user: {
userRecord: {
record: { svn_password },
edit,
save
},
isSaving,
},
} = useContext( GlobalContext );

const handleGenerate = useCallback( async () => {
try {
await edit( { svn_password: 'regenerate' } );
await save();
} catch ( error ) {
let message = error.message;

console.log( error );
}
}, [] );

const handleCopy = useCallback( () => {
navigator.clipboard.writeText( svn_password );
alert( "Copied to clipboard" );
}, [ ] );

return (
<>
<p>
Your SVN password can be used to commit to WordPress.org SVN repositories, such as for a plugin or theme.
</p>
<p>
If you forget your SVN password, you can generate a new one here. Never share your SVN password with anyone.
</p>

{ ( svn_password && 'string' == typeof svn_password ) && (
<Notice status="success" isDismissible={ true }>
<Icon icon={ check } />
New Password generated: <code>{ svn_password }</code>
<Icon icon={ copySmall } onClick={ handleCopy } className="wporg-2fa__svn-copy-password" label="Copy to clipboard" />
</Notice>
) }

<p className="wporg-2fa__submit-actions">
<Button
variant="primary"
onClick={ handleGenerate }
disabled={ isSaving }
>
{ isSaving ? (
<>
<Spinner />
Requesting..
</>
) : (
svn_password ? 'Regenerate password' : 'Request password'
) }
</Button>
</p>
</>
);
}
5 changes: 5 additions & 0 deletions settings/src/components/svn-password.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.wporg-2fa__svn-password {
.wporg-2fa__svn-copy-password {
cursor: pointer;
}
}
2 changes: 2 additions & 0 deletions settings/src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import WebAuthn from './components/webauthn/webauthn';
import BackupCodes from './components/backup-codes';
import GlobalNotice from './components/global-notice';
import RevalidateModal from './components/revalidate-modal';
import SVNPassword from './components/svn-password'

export const GlobalContext = createContext( null );

Expand Down Expand Up @@ -75,6 +76,7 @@ function Main( { userId } ) {
totp: <TOTP />,
'backup-codes': <BackupCodes />,
webauthn: <WebAuthn />,
'svn-password': <SVNPassword />,
};

// The screens where a recent two factor challenge is required.
Expand Down
1 change: 1 addition & 0 deletions settings/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,4 @@ $alert-blue: #72aee6;
@import "components/auto-tabbing-input";
@import "components/revalidate-modal";
@import "components/success";
@import "components/svn-password";
Loading

0 comments on commit 902f846

Please sign in to comment.