Completed
Push — master ( 6e1cad...d0be90 )
by Florent
02:47
created

Signer::getNewInstructions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 16
rs 9.4286
cc 2
eloc 10
nc 2
nop 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2015 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace SpomkyLabs\JoseBundle\Service;
13
14
use Base64Url\Base64Url;
15
use Jose\Algorithm\JWAManagerInterface;
16
use Jose\Algorithm\Signature\SignatureInterface;
17
use Jose\Behaviour\HasJWAManager;
18
use Jose\Behaviour\HasKeyChecker;
19
use Jose\Behaviour\HasPayloadConverter;
20
use Jose\JSONSerializationModes;
21
use Jose\Object\JWKInterface;
22
use Jose\Object\SignatureInstruction;
23
use Jose\Object\SignatureInstructionInterface;
24
use Jose\Payload\PayloadConverterManagerInterface;
25
use Jose\SignerInterface;
26
use Jose\Util\Converter;
27
use SpomkyLabs\JoseBundle\Model\JotInterface;
28
use SpomkyLabs\JoseBundle\Model\JotManagerInterface;
29
30
/**
31
 */
32
final class Signer implements SignerInterface
33
{
34
    /**
35
     * @var null|\SpomkyLabs\JoseBundle\Model\JotManagerInterface
36
     */
37
    private $jot_manager;
38
39
    /**
40
     * @var \Jose\Signer
41
     */
42
    private $signer;
43
44
    /**
45
     * Signer constructor.
46
     *
47
     * @param \Jose\Algorithm\JWAManagerInterface                   $jwa_manager
48
     * @param \Jose\Payload\PayloadConverterManagerInterface        $payload_converter_manager
49
     * @param \SpomkyLabs\JoseBundle\Model\JotManagerInterface|null $jot_manager
50
     */
51
    public function __construct(JWAManagerInterface $jwa_manager,
52
                                PayloadConverterManagerInterface $payload_converter_manager,
53
                                JotManagerInterface $jot_manager = null
54
    ) {
55
        $this->jot_manager = $jot_manager;
56
        $this->signer = new \Jose\Signer($jwa_manager, $payload_converter_manager);
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function sign($input, array $instructions, $serialization, $detached_signature = false, &$detached_payload = null)
63
    {
64
        if (!$this->jot_manager instanceof JotManagerInterface) {
65
            return $this->signer->sign($input, $instructions, $serialization, $detached_signature, $detached_payload);
66
        }
67
68 View Code Duplication
        if (1 < count($instructions) && $serialization !== JSONSerializationModes::JSON_SERIALIZATION) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69
            $result = [];
70
            foreach($instructions as $instruction) {
71
                $result[] = $this->signData($input, [$instruction], $serialization, $detached_signature, $detached_payload);
72
            }
73
74
            return $result;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $result; (array) is incompatible with the return type declared by the interface Jose\SignerInterface::sign of type string|string[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
75
        } else {
76
            return $this->signData($input, $instructions, $serialization, $detached_signature, $detached_payload);
77
        }
78
    }
79
80
    /**
81
     * Sign an input and convert it into JWS JSON (Compact/Flattened) Serialized representation.
82
     *
83
     * @param \Jose\Object\JWKInterface|\Jose\Object\JWKSetInterface|string|array $input              A JWKInterface/JWKInterface/JWKSetInterface object
84
     * @param \Jose\Object\SignatureInstructionInterface[]                        $instructions       A list of instructions used to sign the input
85
     * @param string                                                              $serialization      Serialization method. If the argument $keys contains more than one private key and value is JSON_COMPACT_SERIALIZATION or JSON_FLATTENED_SERIALIZATION, the result will be an array of JWT.
86
     * @param bool                                                                $detached_signature If true, the payload will be detached and variable $detached_payload will be set
87
     * @param null|string                                                         $detached_payload   The detached payload encoded in Base64 URL safe
88
     *
89
     * @throws \Exception
90
     *
91
     * @return string|string[] The JSON (Compact/Flattened) Serialized representation
92
     */
93 View Code Duplication
    private function signData($input, array $instructions, $serialization, $detached_signature = false, &$detached_payload = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
    {
95
        $jot = $this->jot_manager->createJot();
96
        $instructions = $this->getNewInstructions($instructions, $jot);
97
98
        $jws = $this->signer->sign($input, $instructions, $serialization, $detached_signature, $detached_payload);
99
100
        $jot = $jot->withData($jws);
101
        $this->jot_manager->saveJot($jot);
102
103
        return $jws;
104
    }
105
106
    /**
107
     * @param \Jose\Object\SignatureInstructionInterface[] $instructions A list of instructions used to sign the input
108
     * @param \SpomkyLabs\JoseBundle\Model\JotInterface $jot
109
110
     * @return \Jose\Object\SignatureInstructionInterface[]
111
     */
112
    private function getNewInstructions(array $instructions, JotInterface $jot)
113
    {
114
        $new_instructions = [];
115
        foreach($instructions as $instruction) {
116
            $protected_header = $instruction->getProtectedHeader();
117
            $protected_header['jti'] = $jot->getJti();
118
119
            $new_instructions[] = new SignatureInstruction(
120
                $instruction->getKey(),
121
                $protected_header,
122
                $instruction->getUnprotectedHeader()
123
            );
124
        }
125
126
        return $new_instructions;
127
    }
128
}
129