Associating a post with a user in React and Django using JWT tokens - reactjs

I would like to associate a post with a logged-in user, a React, and Django app.
I have tried this, and am getting an unauthorized error,
settings.py
REST_FRAMEWORK = {
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticated'
# ],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
SIMPLE_JWT = {
'AUTH_HEADER_TYPES': ('JWT',),
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'AUTH_TOKEN_CLASSES': (
'rest_framework_simplejwt.tokens.AccessToken',
)
}
my serializers.py
class PoultryCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Poultry
fields = ('title', 'price', 'category', 'description',
'image', 'county', 'subcounty', 'location', 'contact')
read_only_fields = ['seller']
views.py
#api_view(["POST"])
#authentication_classes([authentication.TokenAuthentication])
#permission_classes([permissions.IsAuthenticated])
def poultrycreate(request):
print(request.user)
parser_classes = (MultiPartParser, FormParser)
serializer = PoultryCreateSerializer(data=request.data)
if serializer.is_valid():
serializer.save(seller=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
# print('error', serializer.errors)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Related

Create a react page accessible at a specific URL on Wordpress

I'm building a WordPress plugin that creates an admin menu and a public page accessible at a specific URL lets say www.mywordpress.com/invite-only
In the root directory, I have wp-react-kickoff.php file like this
<?php
/**
* Plugin Name: Batmobile Design
* Author: Batman
* Author URI:
* Version: 1.0.0
* Description: WordPress React KickOff.
* Text-Domain: wp-react-kickoff
*/
if( ! defined( 'ABSPATH' ) ) : exit(); endif; // No direct access allowed.
/**
* Define Plugins Contants
*/
define ( 'WPRK_PATH', trailingslashit( plugin_dir_path( __FILE__ ) ) );
define ( 'WPRK_URL', trailingslashit( plugins_url( '/', __FILE__ ) ) );
/**
* Loading Necessary Scripts
*/
add_action( 'admin_enqueue_scripts', 'sd_scripts' );
function sd_scripts() {
wp_enqueue_script( 'wp-react-kickoff', WPRK_URL . 'dist/bundle.js', [ 'jquery', 'wp-element' ], wp_rand(), true );
wp_localize_script( 'wp-react-kickoff', 'appLocalizer', [
'apiUrl' => home_url( '/wp-json' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
] );
}
require_once WPRK_PATH . 'classes/class-create-admin-menu.php';
require_once WPRK_PATH . 'classes/class-create-settings-routes.php';
require_once WPRK_PATH . 'classes/class-public-page.php';
Then I have created a folder called classes where I have
class-create-admin-menu.php,
class-create-settings-routes.php,
class-public-page.php,
wprk-public-page-template.php.
Code to create admin page looks like this
<?php
/**
* This file will create admin menu page.
*/
class WPRK_Create_Admin_Page {
public function __construct() {
add_action( 'admin_menu', [ $this, 'create_admin_menu' ] );
}
public function create_admin_menu() {
$capability = 'manage_options';
$slug = 'wprk-settings';
add_menu_page(
__( 'WP React KickOff', 'wp-react-kickoff' ),
__( 'WP React KickOff', 'wp-react-kickoff' ),
$capability,
$slug,
[ $this, 'menu_page_template' ],
'dashicons-buddicons-replies'
);
}
public function menu_page_template() {
echo '<div class="wrap"><div id="wprk-admin-app"></div></div>';
}
}
new WPRK_Create_Admin_Page();
class-create-settings-routes.php looks like this
<?php
/**
* This file will create Custom Rest API End Points.
*/
class WP_React_Settings_Rest_Route {
public function __construct() {
add_action( 'rest_api_init', [ $this, 'create_rest_routes' ] );
}
public function create_rest_routes() {
register_rest_route( 'wprk/v1', '/settings', [
'methods' => 'GET',
'callback' => [ $this, 'get_settings' ],
'permission_callback' => [ $this, 'get_settings_permission' ]
] );
register_rest_route( 'wprk/v1', '/settings', [
'methods' => 'POST',
'callback' => [ $this, 'save_settings' ],
'permission_callback' => [ $this, 'save_settings_permission' ]
] );
}
public function get_settings() {
$firstname = get_option( 'wprk_settings_firstname' );
$lastname = get_option( 'wprk_settings_lastname' );
$email = get_option( 'wprk_settings_email' );
$response = [
'firstname' => $firstname,
'lastname' => $lastname,
'email' => $email
];
return rest_ensure_response( $response );
}
public function get_settings_permission() {
return true;
}
public function save_settings( $req ) {
$firstname = sanitize_text_field( $req['firstname'] );
$lastname = sanitize_text_field( $req['lastname'] );
$email = sanitize_text_field( $req['email'] );
update_option( 'wprk_settings_firstname', $firstname );
update_option( 'wprk_settings_lastname', $lastname );
update_option( 'wprk_settings_email', $email );
return rest_ensure_response( 'success' );
}
public function save_settings_permission() {
return current_user_can( 'publish_posts' );
}
}
new WP_React_Settings_Rest_Route();
The code specific to create Public page looks like this
function wprk_create_public_page() {
$page = [
'post_type' => 'page',
'post_title' => 'WP React KickOff Public Page',
'post_content' => 'asasasas',
'post_status' => 'publish',
'post_author' => 1,
];
$page_id = wp_insert_post( $page );
update_post_meta( $page_id, '_wp_page_template', 'wprk-public-page-template.php' );
}
register_activation_hook( __FILE__, 'wprk_create_public_page' );
And In the ReactFolder, I have App.js, both Settings (This is for admin menu, it is working properly) and Public Page rendered like this
import React from 'react';
import PublicPage from './components/PublicPage';
import Settings from './components/Settings';
function App() {
return(
<React.Fragment>
<Settings />
<PublicPage />
</React.Fragment>
)
}
export default App;
And for testing, let the Public page, look like this
import axios from 'axios';
const apiUrl = appLocalizer.apiUrl;
const nonce = appLocalizer.nonce;
import React from 'react';
function PublicPage(props) {
return (
<div>
<h1>hello world asasa</h1>
</div>
);
}
export default PublicPage;
Currently, both the admin page and public page is coming one below the other. I want the Public page to come at a specific URL that users can visit and not just the admins.
You do not need a function to create a page and insert it into database each time you call it.
Instead, create two pages in wp-admin:
A user-accesible page
An admin page
Give them slug names, in root of your theme directory create templates for these pages. Like, if the admin page has slug react-admin, create a file page-react-admin.php, put the code for the page in there, do it for both pages.
Then you can either restrict the admin page via wp-admin, or, use construction like this:
if ( current_user_can( 'manage_options' ) ) {
//the actual code for the page goes there
}
else {
//display a message that you need to log in
}
Check the WordPress guidelines for developing themes.

Django Password not encrypted, custom user model using (AbstractUser)

In Django I have replaced the user model using (AbstractUser).
Then in the admin panel area the fields are unordered. When generating a new user, the password is not encrypted, the user is saved with the password unencrypted. But then I can't access the admin panel. Returning error the username or password do not match.
In model.py
# from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser
from django.db import models
# UserModel = get_user_model()
class UserAgent_mod(AbstractUser):
phone_number = models.CharField(
max_length=9,
blank=True,
null=True,
)
profile_image = models.ImageField(
upload_to='photos_user_agent',
)
manage_properties = models.ManyToManyField(
to='property_app.Property_mod',
related_name='manage_properties',
blank=True
)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Meta:
verbose_name='User Agent'
verbose_name_plural = 'Users Agents'
In admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
UserModel = get_user_model()
#admin.register(UserModel)
class UserAgentAdmin(admin.ModelAdmin):
list_display = (
'username',
'first_name',
'last_name',
'phone_number',
'email',
)
list_filter = (
'first_name',
'last_name',
)
in settings.py
AUTH_USER_MODEL= 'user_agent_app.UserAgent_mod'
Demo result of the problem
put the following code in your admin.py file:
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAgent_mod as BaseUserAdmin
class UserAgentAdmin(BaseUserAdmin):
fieldsets = BaseUserAdmin.fieldsets
ADDITIONAL_USER_FIELDS = (
(None, {'fields': ('phone_number','profile_image','manage_properties')}),
)
fieldsets = fieldsets + ADDITIONAL_USER_FIELDS
list_display = (
'username',
'first_name',
'last_name',
'phone_number',
'email',
)
list_filter = (
'first_name',
'last_name',
)
admin.site.register(get_user_model, UserAgentAdmin)
Whan you use AbstarctUser and you need to add users via the django admin panel, you should also use the UserCreationForm and UserChangeForm, so the password will be encrypted.
To do so, make a forms.py file in your users application and add the following code to it:
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import UserAgent_mod
class UserCreationForm(UserCreationForm):
# If you want to give superuser privileges to the staff users, override the save method
def save(self, commit=True):
user = self.instance
if user.is_staff:
user.is_superuser = True
return super().save(commit=True)
class Meta:
model = UserAgent_mod
fields = ('email', 'first_name', 'last_name') # add any other field you want
class UserChangeForm(UserChangeForm):
class Meta:
model = UserAgent_mod
fields = ('email', 'first_name', 'last_name') # add any other field you want
Also add the below code to your admin.py:
(change the fields as you want to)
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import UserCreationForm, UserChangeForm
from .models import UserAgent_mod
class UserAdmin(UserAdmin):
add_form = UserCreationForm
form = UserChangeForm
model = UserAgent_mod
list_display = ('email', 'is_staff', 'is_active',)
list_filter = ('email', 'is_staff', 'is_active',)
fieldsets = (
(None, {'fields': ('email', 'password', 'first_name', 'last_name')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'first_name', 'last_name', 'is_staff', 'is_active')}
),
)
search_fields = ('email',)
ordering = ('email',)
admin.site.register(UserAgent_mod, UserAdmin)
You can read more about AbstractUser in this article.

The request object does not contain the required `authentication` attribute when trying to API login

I'm trying to skip CSRF token required middleware for API prefix , Here I'm using cakephp authentication plugin 2.x. I have tried below code to avoid token for api prefix
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue->add(new ErrorHandlerMiddleware(Configure::read('Error')))
$csrf = new CsrfProtectionMiddleware();
$csrf->skipCheckCallback(function ($request) {
// Skip token check for API URLs.
if ($request->getParam('prefix') === 'Api') {
return true;
}
});
// add Authentication after RoutingMiddleware
$middlewareQueue->add($csrf);
}
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$authenticationService = new AuthenticationService([
'unauthenticatedRedirect' => Router::url('/login'),
'queryParam' => 'redirect',
]);
// Load identifiers, ensure we check email and password fields
$identifierSettings = [
'fields' => [
'username' => 'email',
'password' => 'password',
],
];
$authenticationService->loadIdentifier('Authentication.Password', $identifierSettings);
$authenticationService->loadAuthenticator('Authentication.Session');
// Configure form data check to pick email and password
$authenticationService->loadAuthenticator('Authentication.Form', [
'fields' => [
'username' => 'email',
'password' => 'password',
],
// 'loginUrl' => Router::url('/admin'),
]);
return $authenticationService;
}
I have written below controller login action for login
public function login()
{
$result = $this->Authentication->getResult();
if($result->isValid())
{
$user = $result->getData();
$this->set('user',$user);
$this->viewBuilder()->setOption('serialize', 'user');
}
}
Agter send request using postman , I have got below response
I have nothing set in authentication option in postman, Do I need anything to set in postman ? What is this authentication attribute ?

Cakephp 3.7.4 Auth hash password Not working

I am trying to implement auth login and register cakephp 3.7.4
I have used following code for UsersController add method
public function add()
{
$this->viewBuilder()->setLayout('login');
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$post = $this->request->getData();
$post['created'] = date('Y-m-d H:i:s');
$post['modified'] = date('Y-m-d H:i:s');
$user = $this->Users->patchEntity($user, $post);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'login']);
}
$this->Flash->error(__('Unable to add the user.'));
}
$this->set('user', $user);
}
But it can't save password in Hash Format
I have also create entity and used this function but it also not helped me
class User extends Entity
{
protected $_accessible = [
'email' => true,
'password' => true
];
protected $_hidden = [
'password'
];
protected function _setPassword($password){
return(new DefaultPasswordHasher)->hash($password);
}
}
I installed a fresh Cake 3.7.4 installation, baked a users table, users controller and users table/entity. The following code for the User Class successfully hashed the password before it is written to the database. Please compare it with your code.
namespace App\Model\Entity;
use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;
class User extends Entity
{
protected $_accessible = [
'email' => true,
'password' => true,
'modified' => true,
'created' => true
];
protected $_hidden = [
'password'
];
protected function _setPassword($password)
{
return (new DefaultPasswordHasher())->hash($password);
}
}
Just use parenthesis around DefaultPasswordHaser():
return(new DefaultPasswordHasher())->hash($password);

Yii2 REST+ Angular Cross Domain CORS

I have developed Angular & Yii2 REST service. Have problem in cross domain.
Here below add my angular & Yii2 REST Code.
AngularJs : (like 'http://organization1.example.com','http://organization2.example.com',....)
$http.defaults.useXDomain = true;
$http.defaults.withCredentials = true;
$http.defaults.headers.common['Authorization'] = 'Bearer ' + MYTOKEN
My Request from Angular Controller:
apiURL = 'http://api.example.com';
$http.get(apiURL + '/roles')
.success(function (roles) { })
.error(function () { });
Yii2 .htaccess: (REST URL like 'http://api.example.com')
Header always set Access-Control-Allow-Origin: "*"
Header always set Access-Control-Allow-Credentials: true
Header always set Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "Authorization,X-Requested-With, content-type"
Yii2 My Behaviour:
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['corsFilter'] = [
'class' => Cors::className(),
'cors' => [
'Origin' => ['*'],
'Access-Control-Expose-Headers' => [
'X-Pagination-Per-Page',
'X-Pagination-Total-Count',
'X-Pagination-Current-Page',
'X-Pagination-Page-Count',
],
],
];
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['options'],
];
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
];
return $behaviors;
}
Problem
From my angular request is 'GET' method, but it will goes 'OPTIONS' method & return 401 Unauthorized error(CORS). because the request Authorization header is not send.
Update:
As pointed by #jlapoutre, this is now well described in official docs:
Adding the Cross-Origin Resource Sharing filter to a controller is a
bit more complicated than adding other filters described above,
because the CORS filter has to be applied before authentication
methods and thus needs a slightly different approach compared to other
filters. Also authentication has to be disabled for the CORS Preflight
requests so that a browser can safely determine whether a request can
be made beforehand without the need for sending authentication
credentials. The following shows the code that is needed to add the
yii\filters\Cors filter to an existing controller that extends from
yii\rest\ActiveController:
use yii\filters\auth\HttpBasicAuth;
public function behaviors()
{
$behaviors = parent::behaviors();
// remove authentication filter
$auth = $behaviors['authenticator'];
unset($behaviors['authenticator']);
// add CORS filter
$behaviors['corsFilter'] = [
'class' => \yii\filters\Cors::className(),
];
// re-add authentication filter
$behaviors['authenticator'] = $auth;
// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
$behaviors['authenticator']['except'] = ['options'];
return $behaviors;
}
Old Answer (deprecated)
There is an ordering issue when merging with parent::behaviors(). Full details here.
I would recommend not defining keys when merging with parent array:
public function behaviors()
{
return \yii\helpers\ArrayHelper::merge([
[
'class' => \yii\filters\Cors::className(),
'cors' => [...],
],
[
'class' => \yii\filters\auth\HttpBearerAuth::className(),
'except' => ['options'],
],
[
'class' => ContentNegotiator::className(),
'formats' => [...],
]
], parent::behaviors());
}
In your controller:
use yii\filters\Cors;
...
public function behaviors()
{
return array_merge([
'cors' => [
'class' => Cors::className(),
#special rules for particular action
'actions' => [
'your-action-name' => [
#web-servers which you alllow cross-domain access
'Origin' => ['*'],
'Access-Control-Request-Method' => ['POST'],
'Access-Control-Request-Headers' => ['*'],
'Access-Control-Allow-Credentials' => null,
'Access-Control-Max-Age' => 86400,
'Access-Control-Expose-Headers' => [],
]
],
#common rules
'cors' => [
'Origin' => [],
'Access-Control-Request-Method' => [],
'Access-Control-Request-Headers' => [],
'Access-Control-Allow-Credentials' => null,
'Access-Control-Max-Age' => 0,
'Access-Control-Expose-Headers' => [],
]
],
], parent::behaviors());
}
Documentation
issue with your code you are not unsetting auth at very start
public function behaviors() {
$behaviors = parent::behaviors();
/*unset here*/
unset($behaviors['authenticator']);
$behaviors['corsFilter'] = [
'class' => Cors::className(),
'cors' => [
'Origin' => ['*'],
'Access-Control-Expose-Headers' => [
'X-Pagination-Per-Page',
'X-Pagination-Total-Count',
'X-Pagination-Current-Page',
'X-Pagination-Page-Count',
],
],
];
/*re-set here*/
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['options'],
];
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
];
return $behaviors;
}

Resources