0

Symfony2 で FOSRest および FOSOauthServer バンドル (... およびその他多数) を使用して RESTful Web サービスを開発しています。私の問題は、他のユーザーのアクセス トークンを使用すると、API が 403 ステータス コードではなく応答を返すことです。例えば:

データベースに2人のユーザーが保存されています

userA with tokenA userB with tokenB

リクエスト例 http://example.com/api/v1/userA/products?access_token=tokenB

現在の応答

{
   products: {
    0: { ... } 
    1: { ... }
  }
}

しかし、ユーザー B のアクセス トークンを使用してユーザー A の製品を要求しています。提供されたアクセス トークンが製品の所有者のものであるかどうかを確認するにはどうすればよいですか??

私の security.yml ファイル:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        MY_ROLE:
            # ...
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT
    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:            
        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                csrf_provider:  form.csrf_provider
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
            logout: 
                path:           /admin/logout
            anonymous:          true                

        # FOSOAuthBundle and FOSRestBundle    
        oauth_token:
            pattern:    ^/oauth/v2/token
            security:   false

#        oauth_authorize: commented because there are not oauth login form on this app
#            pattern:    ^/oauth/v2/auth
            # Add your favorite authentication process here

        api:
            pattern:    ^/api
            fos_oauth:  true
            stateless:  true
            anonymous:  false

        # This firewall is used to handle the public login area
        # This part is handled by the FOS User Bundle
        main:
            # ...

    access_control:
        # ...

        # API (FOSRestBundle and FOSOAuthBundle)
        - { path: ^/api, roles: [IS_AUTHENTICATED_FULLY] }

ApiBundle の routing.yml

# API Endpoints
app_api_user_get_products:
    pattern: /{username}/products
    defaults: { _controller: ApiBundle:User:getProducts, _format: json }
    methods: GET

私の UserController.php

<?php

namespace App\ApiBundle\Controller;

Use App\MainBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ... more use statments

    class UserController extends ApiController {

    /**
     * List user's products.
     *
     * @ApiDoc(
     *  resource = true,
     *  description="This method must have the access_token parameter. The parameters limit and offset are optional.",
     *  filters={
     *      {"name"="access_token", "dataType"="string", "required"="true"},
     *      {"name"="offset", "dataType"="integer", "default"="0", "required"="false"},
     *      {"name"="limit", "dataType"="integer", "default"="250", "required"="false"}
     *  },
     * )
     *
     * @Annotations\QueryParam(name="offset", requirements="\d+", nullable=true, description="Offset from which to start listing products.")
     * @Annotations\QueryParam(name="limit", requirements="\d+", default="500", description="How many products to return.")
     *
     * @Annotations\View()
     *
     * @param User               $user      the request object
     * @param ParamFetcherInterface $paramFetcher param fetcher service
     *
     * @return array
     */
    public function getProductsAction(User $user, ParamFetcherInterface $paramFetcher, Request $request) { 


//        $offset = $paramFetcher->get('offset');
//        $offset = null == $offset ? 0 : $offset;
//        $limit = $paramFetcher->get('limit');

        try {
            // estructure and exclude fields strategy http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies
            $data = array('products' => array());
            foreach ($user->getCatalog() as $p) {
                if ($p->getAvailable() == true) {
                    $product = $p->getProduct();
                    $data['products'][] = array(
                        'id' => $product->getId(),
                        'reference' => $product->getReference(),
                        'brand' => $product->getBrand(),
                        'description' => $product->getDescription(),
                        // ...
                    );
                }
            }
        } catch (\Exception $e) {
            throw new HttpException(Codes::HTTP_INTERNAL_SERVER_ERROR, $e->getTraceAsString());
        }

        // New view
        $view = new View($data);
        $view->setFormat('json');

        return $this->handleView($view);
    }
}

助けてくれてどうもありがとう!

4

2 に答える 2

1

私は解決策を見つけました。簡単です。残りのコントローラーに次のコードを追加し、app/config.yml に構成パラメーターを追加しただけです。

UserController.php

...
public function getProductsAction(User $user, ParamFetcherInterface $paramFetcher, Request $request) {

        // Check if the access_token belongs to the user making the request
        $requestingUser = $this->get('security.context')->getToken()->getUser();
        if (!$requestingUser || $requestingUser !== $user) {
                throw new AccessDeniedHttpException();
        }
...

~/app/config.yml

# FOSRestBundle
fos_rest:
    routing_loader:
        default_format: json
    param_fetcher_listener: true
    view:
        view_response_listener: force
    access_denied_listener: # I've added this
        # all requests using the 'json' format will return a 403 on an access denied violation
        json: true
于 2014-05-29T11:14:43.913 に答える
0

Symfony >= 2.4 で @Security アノテーションを使用して、より簡単にすることもできます。あなたの場合、それは次のようになります

/**
 * @Security("user.getId() == userWithProducts.getId()")
 */

およびアクションヘッダー:

...
public function getProductsAction(User $userWithProducts, ParamFetcherInterface $paramFetcher, Request $request) {
...
于 2014-09-01T14:20:29.953 に答える